├── .gitignore
├── LICENSE
├── README.md
└── etymology
├── .gitattributes
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── _index.html
├── components
├── action.js
├── idyll-footer.js
└── trig-display.js
├── images
├── logo.png
└── logo.svg
├── index.idl
├── package.json
└── styles.css
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | build
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Matthew Conlen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # trig
2 | interactive documents related to trigonometry
3 |
--------------------------------------------------------------------------------
/etymology/.gitattributes:
--------------------------------------------------------------------------------
1 | data/consumer-complaints-small.csv filter=lfs diff=lfs merge=lfs -text
2 | data/consumer-complaints.csv filter=lfs diff=lfs merge=lfs -text
3 |
--------------------------------------------------------------------------------
/etymology/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | *.csv
40 |
41 | env
42 |
43 | .idyll
--------------------------------------------------------------------------------
/etymology/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "python.linting.pylintEnabled": false
4 | }
--------------------------------------------------------------------------------
/etymology/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Matthew Conlen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/etymology/README.md:
--------------------------------------------------------------------------------
1 | # etymology
2 |
3 |
--------------------------------------------------------------------------------
/etymology/_index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Etymology of Trig
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/etymology/components/action.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const IdyllComponent = require('idyll-component');
3 |
4 | class Action extends IdyllComponent {
5 | render() {
6 | return (
7 | {this.props.children}
8 | );
9 | }
10 | }
11 |
12 | module.exports = Action;
13 |
--------------------------------------------------------------------------------
/etymology/components/idyll-footer.js:
--------------------------------------------------------------------------------
1 |
2 | const React = require('react');
3 | const IdyllComponent = require('idyll-component');
4 |
5 | class Logo extends IdyllComponent {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | This document was created with Idyll ,
12 | a new markup language for creating interactive documents.
13 | See the markup used to create this page .
14 |
15 |
16 | );
17 | }
18 | }
19 |
20 | module.exports = Logo;
21 |
--------------------------------------------------------------------------------
/etymology/components/trig-display.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const IdyllComponent = require('idyll-component');
3 | const Animate = require('victory-core').VictoryAnimation;
4 | const Line = require('victory').VictoryLine;
5 | const d3 = require('d3-scale');
6 | const d3Shape = require('d3-shape');
7 | const d3Arr = require('d3-array');
8 |
9 | const polarToCartesian = (centerX, centerY, radius, angle) => {
10 | return {
11 | x: centerX + (radius * Math.cos(angle)),
12 | y: centerY + (radius * Math.sin(angle))
13 | };
14 | }
15 |
16 | const describeArc = (x, y, radius, startAngle, endAngle) => {
17 | var start = polarToCartesian(x, y, radius, startAngle);
18 | var end = polarToCartesian(x, y, radius, endAngle);
19 |
20 | var largeArcFlag = startAngle < -Math.PI ? 1 : 0;
21 | var sweepFlag = startAngle < -Math.PI ? 1 : 1;
22 |
23 | var d = [
24 | "M", start.x, start.y,
25 | "A", radius, radius, 0, largeArcFlag, sweepFlag, end.x, end.y
26 | ].join(" ");
27 |
28 | return d;
29 | }
30 |
31 | const getPath = (f) => {
32 | const line = d3Shape.line().x((d) => d).y((d) => f(d));
33 | return line(d3Arr.range(-Math.PI, Math.PI, 0.001));
34 | }
35 |
36 | const functionDisplay = (f, theta, scaleY) => {
37 | return null;
38 | scaleY = scaleY || 1;
39 | return (
40 |
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | const WIDTH = 3;
48 | const CENTER = WIDTH / 2;
49 |
50 | const formatNumber = (x, n) => {
51 | return x.toFixed(n);
52 | };
53 |
54 | class TrigDisplay extends IdyllComponent {
55 |
56 | constructor(props) {
57 | super(props);
58 | this.state = {
59 | thetaFixed: false,
60 | fFixed: false
61 | }
62 | }
63 |
64 | renderStage(stage) {
65 | const theta = this.state.thetaFixed || this.props.theta;
66 | switch(stage) {
67 | case 'sin':
68 | return ;
69 | case 'cos':
70 | return ;
71 | case 'tan':
72 | return ;
73 | case 'cotan':
74 | return ;
75 | case 'sec':
76 | return ;
77 | case 'cosec':
78 | return ;
79 | case 'chord':
80 | return ;
81 | }
82 | }
83 |
84 | handleClick(evt) {
85 | if (this.state.thetaFixed) {
86 | this.setState({
87 | thetaFixed: false
88 | })
89 | } else {
90 | this.pt.x = evt.clientX;
91 | this.pt.y = evt.clientY;
92 | const loc = this.pt.matrixTransform(this.svg.getScreenCTM().inverse());
93 | loc.x -= CENTER;
94 | loc.y = CENTER - loc.y;
95 | const theta = Math.atan2(loc.y, loc.x);
96 | this.setState({
97 | thetaFixed: theta
98 | });
99 | }
100 | }
101 |
102 | handleMouseMove(evt) {
103 | if (this.state.thetaFixed) {
104 | return;
105 | }
106 | this.pt.x = evt.clientX;
107 | this.pt.y = evt.clientY;
108 | const loc = this.pt.matrixTransform(this.svg.getScreenCTM().inverse());
109 | loc.x -= CENTER;
110 | loc.y = CENTER - loc.y;
111 | const theta = Math.atan2(loc.y, loc.x);
112 | this.updateProps({
113 | theta: theta
114 | });
115 | }
116 |
117 | handleStatMouseover(name) {
118 | return (evt) => {
119 | this.updateProps({
120 | stage: name
121 | });
122 | }
123 | }
124 |
125 | handleStatClick(name) {
126 | return (evt) => {
127 | if (this.state.fFixed === name) {
128 | this.setState({
129 | fFixed: false
130 | });
131 | } else {
132 | this.setState({
133 | fFixed: name
134 | });
135 | }
136 | }
137 | }
138 |
139 | handleMouseLeaveStates(evt) {
140 | this.updateProps({
141 | stage: this.state.fFixed
142 | });
143 | }
144 |
145 | render() {
146 | const { stage } = this.props;
147 |
148 | const stats = [
149 | {name: 'sin', f: Math.sin},
150 | {name: 'cos', f: Math.cos},
151 | {name: 'tan', f: Math.tan},
152 | {name: 'cotan', f: (x) => 1 / Math.tan(x)},
153 | {name: 'sec', f: (x) => 1 / Math.cos(x)},
154 | {name: 'cosec', f: (x) => 1 / Math.sin(x)}
155 | ]
156 |
157 | const theta = this.state.thetaFixed || this.props.theta;
158 | const arcTheta = theta > 0 ? theta : (theta + 2 * Math.PI);
159 |
160 | return (
161 |
162 |
{if (!svg) return; this.svg = svg; this.pt = svg.createSVGPoint()}}>
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | θ
171 |
172 | {
173 | this.state.thetaFixed ?
174 | ( ) : null
175 | }
176 |
177 | {this.renderStage(stage)}
178 |
179 |
180 |
181 |
182 | {stats.map((stat) => {
183 | let className = '';
184 | if (stage === stat.name) {
185 | className += 'selected';
186 | }
187 | if (this.state.fFixed && stage === this.state.fFixed && stat.name === this.state.fFixed) {
188 | className += ' sticky';
189 | }
190 | return (
191 |
196 | {stat.name}({formatNumber(theta, 2)}) = {formatNumber(stat.f(theta), 4)}
197 |
198 | );
199 | })
200 | }
201 |
202 |
203 | Click to select angle or function
204 |
205 |
206 | );
207 | }
208 | }
209 |
210 | const overlayGenerator = (defaultState, initial, end, color) => {
211 | const initialState = Object.assign({}, defaultState, initial);
212 | const endState = Object.assign({}, defaultState, end);
213 |
214 | class Overlay extends React.PureComponent {
215 | constructor(props) {
216 | super(props);
217 | this.state = {
218 | progress: 0
219 | };
220 | }
221 |
222 | render() {
223 | const { theta } = this.props;
224 | const scales = {
225 | x1: d3.scaleLinear().range([initialState.x1(theta), endState.x1(theta)]),
226 | x2: d3.scaleLinear().range([initialState.x2(theta), endState.x2(theta)]),
227 | y1: d3.scaleLinear().range([initialState.y1(theta), endState.y1(theta)]),
228 | y2: d3.scaleLinear().range([initialState.y2(theta), endState.y2(theta)])
229 | };
230 | return (
231 |
232 | {
233 | ((tweenedProps, animationInfo) => {
234 | const { progress } = tweenedProps;
235 | return (
236 | { this.setState({progress: 1}) }}>
237 |
238 |
239 |
240 |
241 | );
242 | })
243 | }
244 |
245 | );
246 | }
247 | }
248 | return Overlay;
249 | }
250 |
251 | const SinOverlay = overlayGenerator({
252 | x1: (x) => CENTER + Math.cos(x),
253 | x2: (x) => CENTER + Math.cos(x),
254 | y1: (x) => CENTER
255 | }, {
256 | y2: (x) => CENTER
257 | }, {
258 | y2: (x) => CENTER - Math.sin(x)
259 | }, 'red');
260 |
261 | const CosOverlay = overlayGenerator({
262 | x1: (x) => CENTER,
263 | y1: (x) => CENTER - Math.sin(x),
264 | y2: (x) => CENTER - Math.sin(x),
265 | }, {
266 | x2: (x) => CENTER
267 | }, {
268 | x2: (x) => CENTER + Math.cos(x)
269 | }, 'blue');
270 |
271 | const TanOverlay = overlayGenerator({
272 | x1: (x) => CENTER + Math.cos(x),
273 | y1: (x) => CENTER - Math.sin(x)
274 | }, {
275 | x2: (x) => CENTER + Math.cos(x),
276 | y2: (x) => CENTER - Math.sin(x)
277 | }, {
278 | x2: (x) => CENTER + 1 / Math.cos(x),
279 | y2: (x) => CENTER
280 | }, 'green');
281 |
282 | const CotanOverlay = overlayGenerator({
283 | x1: (x) => CENTER + Math.cos(x),
284 | y1: (x) => CENTER - Math.sin(x)
285 | }, {
286 | x2: (x) => CENTER + Math.cos(x),
287 | y2: (x) => CENTER - Math.sin(x)
288 | }, {
289 | x2: (x) => CENTER,
290 | y2: (x) => CENTER - 1 / Math.sin(x)
291 | }, 'green');
292 |
293 | const SecOverlay = overlayGenerator({
294 | x1: (x) => CENTER,
295 | y1: (x) => CENTER,
296 | y2: (x) => CENTER
297 | }, {
298 | x2: (x) => CENTER
299 | }, {
300 | x2: (x) => CENTER + 1 / Math.cos(x)
301 | }, 'red');
302 |
303 | const CosecOverlay = overlayGenerator({
304 | x1: (x) => CENTER,
305 | y1: (x) => CENTER,
306 | x2: (x) => CENTER
307 | }, {
308 | y2: (x) => CENTER
309 | }, {
310 | y2: (x) => CENTER - 1 / Math.sin(x)
311 | }, 'green');
312 |
313 |
314 | class ChordOverlay extends React.PureComponent {
315 | constructor(props) {
316 | super(props);
317 | this.state = {
318 | progress: 0
319 | };
320 | }
321 |
322 | render() {
323 | const { theta } = this.props;
324 | const defaultState = {
325 | x1: (x) => CENTER + Math.cos(x),
326 | x2: (x) => CENTER + Math.cos(x),
327 | y1: (x) => CENTER - Math.sin(x)
328 | };
329 | const initialState = Object.assign({}, {
330 | y2: (x) => CENTER - Math.sin(x)
331 | }, defaultState);
332 | const endState = Object.assign({}, {
333 | y2: (x) => CENTER
334 | }, defaultState);
335 |
336 | const scales = {
337 | x1: d3.scaleLinear().range([initialState.x1(theta), endState.x1(theta)]),
338 | x2: d3.scaleLinear().range([initialState.x2(theta), endState.x2(theta)]),
339 | y1: d3.scaleLinear().range([initialState.y1(theta), endState.y1(theta)]),
340 | y2: d3.scaleLinear().range([initialState.y2(theta), endState.y2(theta)]),
341 | theta: d3.scaleLinear().range([0, theta]),
342 | };
343 |
344 | return (
345 |
346 |
347 |
348 |
349 |
350 | {
351 | ((tweenedProps, animationInfo) => {
352 | const { progress } = tweenedProps;
353 |
354 | return (
355 | { this.setState({progress: 1}) }}>
356 |
357 |
358 |
359 |
360 | );
361 | })
362 | }
363 |
364 |
365 | );
366 | }
367 | }
368 |
369 | TrigDisplay.defaultProps = {
370 | theta: Math.PI / 4,
371 | overlays: ['cos']
372 | };
373 |
374 | module.exports = TrigDisplay;
--------------------------------------------------------------------------------
/etymology/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mathisonian/trig/efa3e4da12ce56d425c30c9d9881f3285dd6863b/etymology/images/logo.png
--------------------------------------------------------------------------------
/etymology/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Artboard Copy 11
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/etymology/index.idl:
--------------------------------------------------------------------------------
1 | [IdyllLogo /]
2 |
3 | [Header
4 | title:"The Etymology of Trig Functions"
5 | author:"Matthew Conlen"
6 | authorLink:"https://twitter.com/mathisonian" /]
7 |
8 | [section className:"article-body"]
9 |
10 | [var name:"theta" value:`Math.PI / 4` /]
11 | [var name:"stage" value:`` /]
12 |
13 | [i]Ed. Note: Mouseover the blue links to see concepts demonstrated visually.[/i]
14 |
15 | You may have learned about trigonometric functions such as **sine** and **cosine** as being defined by
16 | the ratios of sides of a triangle (*SOHCAHTOA*),
17 | or in terms of points and lines related to the unit circle. For me, it didn't totally click until I started to think visually about
18 | inscribing a triangle inside of a circle.
19 |
20 | For example, you can think about the sine function as
21 | measuring [action onMouseOver:`stage = 'sin'`]the distance from the x-axis[/action] of a point
22 | on the unit circle at a particular angle. The sign (+/-) of that value indicates if the point lies above
23 | or below the axis. Similarly the cosine can be
24 | thought of as measuring [action onMouseOver:`stage = 'cos'`]the distance from the y-axis[/action] of
25 | that same point.
26 |
27 | It is useful to note that the cosine of an angle is the same as the sine of the complement of the angle. In other words,
28 | it is the same operation as sine, just with respect to the y-axis instead of the x-axis.
29 |
30 | The word *sine* originally came from the latin *sinus*, meaning "bay" or "inlet". However, it had a
31 | long path to get there. The [link text:"earliest known reference" href:"http://jeff560.tripod.com/s.html" /] to the sine function is from [link text:"Aryabhata the Elder" href:"https://en.wikipedia.org/wiki/Aryabhata" /],
32 | who used both *ardha-jya* (half-chord) and *jya* (chord) to mean *sine* in
33 | Aryabhatiya, a Sanskrit text finished in 499 CE.
34 |
35 | Jya, meaning chord, became *jiba* in Arabic, and was abbreviated as just *jb*. When the term was translated to latin in the
36 | twelfth century, *jb* was incorrectly read as *jaib* (meaning "bay" or "inlet"), and thus translated as *sinus*.
37 |
38 | The sine function has a [action onMouseOver:`stage='chord'; theta = Math.PI / 4;`]direct connection to chords on a circle[/action]. Pick two points on the unit circle,
39 | then the length of the line connecting the two points is exactly twice the value of the sine of half the
40 | anlge between them. That is, `chord_length(θ) / 2 = sin(θ / 2)`.
41 |
42 | **Tangent** comes from the latin *tangere*, the verb meaning "to touch". A line tangent to a circle intersects it at exactly one point.
43 | From this, a [action onMouseOver:`stage = 'tan'`]geometric construction of the tangent function[/action] makes a lot of sense: take the line tangent from a point on the unit
44 | circle and calculate the distance along that line from the point of intersection with the circle, to the point of
45 | intersection with the x-axis. Similarly, the distance from that same point on the unit circle [action onMouseOver:`stage = 'cotan'`]to the y-axis[/action] is
46 | the value of the **cotangent** function.
47 |
48 | The origin of **secant** can be traced to latin as well. It comes from the latin word *secare*, meaning "to cut".
49 | By definition a secant line on a circle is any line that intersects it in two places, you can think of this
50 | line as cutting the circle in two pieces. The secant function is the distance [action onMouseOver:`stage = 'sec'`]from the origin
51 | to the point where the tangent line intercepts the x-axis[/action]. Note that if this secant line is extended, it cuts the
52 | unit circle neatly in half. Again, the **cosecant** can be thought of as being the same as [action onMouseOver:`stage = 'cosec'`]the same function
53 | with respect to the y-axis[/action].
54 |
55 | [desktop]
56 | If you want to explore more move your mouse over the unit circle to the right. Move your mouse to change the displayed angle and select different
57 | trigonometric functions. Click to lock in a particular angle or function.
58 | [/desktop]
59 |
60 | [fixed]
61 |
62 | [TrigDisplay
63 | theta:theta
64 | stage:stage /]
65 |
66 | [/fixed]
67 |
68 | [IdyllFooter markupUrl:"https://github.com/mathisonian/trig/blob/master/etymology/index.idl" /]
69 |
70 | [/section]
71 |
72 |
--------------------------------------------------------------------------------
/etymology/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "consumer-complaints",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "start": "idyll index.idl --css styles.css",
6 | "build": "mkdir ../build; mkdir ../build/etymology; cp _index.html ../build/etymology/index.html; cp styles.css ../build/etymology/; cp -r fonts ../build/etymology/; cp -r images ../build/etymology/; idyll index.idl --build | uglifyjs > ../build/etymology/index.js",
7 | "deploy": "gh-pages -d ../build",
8 | "disc": "discify build/index.js > disc.html && open disc.html"
9 | },
10 | "dependencies": {
11 | "csv-parser": "^1.11.0",
12 | "d3-selection": "^1.0.5",
13 | "idyll": "^1.0.0",
14 | "react-inlinesvg": "^0.5.5",
15 | "react-vega-lite": "0.0.1",
16 | "reactable": "^0.14.1",
17 | "victory-core": "^14.0.7"
18 | },
19 | "devDependencies": {
20 | "disc": "^1.3.2",
21 | "gh-pages": "^0.12.0",
22 | "uglify-js": "^2.8.12"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/etymology/styles.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Archivo+Black|Fira+Mono:400,500,700|Source+Sans+Pro:300,300i,400,400i,600,600i,700,700i');
2 |
3 | @font-face {
4 | font-family: EskellDisplayStencil;
5 | src: url("./fonts/EskellDisplay/EksellDisplayStencil.otf") format("opentype");
6 |
7 | font-weight: normal;
8 | }
9 |
10 | /*
11 | font-family: 'Source Sans Pro', sans-serif;
12 | font-family: 'Archivo Black', sans-serif;
13 | font-family: 'Fira Mono', monospace;
14 | */
15 |
16 | * {
17 | box-sizing: border-box;
18 | }
19 |
20 | html {
21 | margin: 0;
22 | padding: 0;
23 | }
24 |
25 | img {
26 | display: block;
27 | width: 100%;
28 | }
29 |
30 | body {
31 | margin: 0;
32 | padding: 0;
33 | color: black;
34 | font-family: 'Source Sans Pro', sans-serif;
35 | /*background: #00edc5;*/
36 | }
37 |
38 | h1,h2,h3,h4,h5,h6{
39 | margin: 40px 0 20px 0;
40 | font-family: 'Source Sans Pro', sans-serif;
41 | font-weight: bold;
42 | }
43 |
44 | p, .article-body {
45 | font-size: 1.15rem;
46 | line-height: 1.75rem;
47 | }
48 |
49 | .logo-lockup {
50 | display: block;
51 | position: fixed;
52 | top: 10px;
53 | left: 10px;
54 | }
55 |
56 | .logo-lockup a {
57 | display: block;
58 | cursor: pointer;
59 | }
60 |
61 | .logo-lockup a .logo {
62 | width: 60px;
63 | height: 60px;
64 | background: #EAE7D6;
65 | }
66 |
67 | .logo-lockup a small {
68 | display: block;
69 | transform: rotate(-90deg);
70 | margin-top: 0;
71 | font-size: 1rem;
72 | line-height: 60px;
73 | opacity: 0;
74 | padding: 0 20px;
75 | top: 60px;
76 | left: -30px;
77 | position: fixed;
78 | z-index: -1;
79 | transition: all .25s ease-in-out;
80 | }
81 |
82 | .logo-lockup a:hover small {
83 | opacity: 1;
84 | top: 120px;
85 | }
86 |
87 | .article {
88 | max-width: 90vw;
89 | margin: 0 auto;
90 | padding: 60px 0;
91 | margin-bottom: 60px;
92 | }
93 |
94 |
95 | .section {
96 | padding: 0 10px;
97 | margin: 0 auto;
98 | }
99 |
100 | .article-header {
101 | width: 600px;
102 | margin-left: 10vw;
103 | }
104 |
105 | .hed {
106 | font-family: 'EskellDisplayStencil', sans-serif;
107 | font-size: 3rem;
108 | line-height: 3rem;
109 | font-weight: 100;
110 | margin: 20px 0 20px;
111 | width: 150%;
112 | max-width: 90vw;
113 | }
114 |
115 | .dek {
116 | font-family: 'Source Sans Pro', sans-serif;
117 | margin: 0;
118 | display: block;
119 | font-size: 1.5rem;
120 | line-height: 2.2rem;
121 | color: black;
122 | margin-top: 1rem;
123 | }
124 |
125 | .byline {
126 | font-family: 'Fira Mono', monospace;
127 | font-size: .95rem;
128 | line-height: 1rem;
129 | color: black;
130 | margin-top: 1rem;
131 | }
132 |
133 | a, a:visited {
134 | color: black;
135 | cursor: pointer;
136 | text-decoration: none;
137 | /*border-bottom: 1px solid #EAE7D6;*/
138 | box-shadow: inset 0 -4px 0 #EAE7D6;
139 | transition: box-shadow 0.25s ease-out;
140 | }
141 |
142 | a:hover {
143 | color: black;
144 | /*background: #EAE7D6;*/
145 | box-shadow: inset 0 -20px 0 #EAE7D6;
146 | }
147 |
148 | a tspan {
149 | fill: #350bd5;
150 | text-decoration: underline;
151 | }
152 |
153 | .byline a {
154 | color: black;
155 | }
156 |
157 | .article-body {
158 | width: 600px;
159 | margin-left: 10vw;
160 | margin-top: 45px;
161 | }
162 |
163 | pre {
164 | margin: 25px 0;
165 | width: 100%;
166 | }
167 |
168 | pre code {
169 | background: #F2F3F2;
170 | color: black;
171 | padding: 20px 15px;
172 | width: 100%;
173 | display: block;
174 | overflow-x: auto;
175 | }
176 | code {
177 | background: #F2F3F2;
178 | color: black;
179 | padding: 1px 5px;
180 | }
181 |
182 | .inset {
183 | max-width: 400px;
184 | margin: 0 auto;
185 | }
186 |
187 | input {
188 | cursor: pointer;
189 | }
190 |
191 | .relative {
192 | position: relative;
193 | }
194 | .aside {
195 | position: absolute;
196 | width: 300px;
197 | right: calc((10vw + 600px + 150px) / -2);
198 | }
199 |
200 | svg.trig-display line,
201 | svg.trig-display circle,
202 | svg.trig-display path,
203 | .stats-container svg line,
204 | .stats-container svg path {
205 | vector-effect: non-scaling-stroke;
206 | fill: none;
207 | stroke: black;
208 | stroke-width: 2px;
209 | }
210 |
211 | .fixed {
212 | position: fixed;
213 | display: flex;
214 | align-self: center;
215 | /*flex-direction: column;*/
216 | align-items: center;
217 | right: 25px;
218 | top: 0;
219 | bottom: 0;
220 | width: calc((80vw - 600px) - 50px);
221 | }
222 |
223 | .fixed div {
224 | width: 100%;
225 | }
226 | .fixed svg {
227 | cursor: crosshair;
228 | }
229 |
230 | span.action {
231 | border-color: #5601FF;
232 | border-width: 2px;
233 | border-style: none none solid none;
234 | color: #5601FF;
235 | /*font-size: 0.9em;*/
236 | padding: -4px 5px;
237 | margin: 0 5px;
238 | cursor: pointer;
239 | }
240 |
241 | .stats-container {
242 | display: flex;
243 | flex-direction: row;
244 | width: 100%;
245 | flex-wrap: wrap;
246 | font-family: 'Fira Mono', monospace;
247 | font-size: 11px;
248 | margin-bottom: 10px;
249 | }
250 | .stats-container div {
251 | width: 50%;
252 | white-space:nowrap;
253 | text-align: center;
254 | cursor: pointer;
255 | }
256 | .stats-container .selected {
257 | font-weight: bold;
258 | }
259 | .stats-container .sticky {
260 | text-decoration: underline;
261 | }
262 |
263 | .instructions {
264 | text-align: center;
265 | color: #888;
266 | font-size: 12px;
267 | text-transform: uppercase;
268 | }
269 |
270 | @media all and (max-width: 1600px) {
271 | .article-header {
272 | margin-left: 5vw;
273 | }
274 | .article-body {
275 | margin-left: 5vw;
276 | }
277 |
278 | .fixed {
279 | width: calc((85vw - 600px) - 50px);
280 | }
281 | }
282 |
283 |
284 | @media all and (max-width: 1200px) {
285 | /* put your css styles in here */
286 | .article-header {
287 | margin-left: 10px;
288 | width: 500px;
289 | }
290 | .article-body {
291 | margin-left: 10px;
292 | width: 500px;
293 | }
294 |
295 | .fixed {
296 | width: calc((500px - 10px) - 50px);
297 | }
298 | }
299 |
300 |
301 | @media all and (max-width: 1000px) {
302 | /* put your css styles in here */
303 | .desktop {
304 | display: none;
305 | }
306 | .relative {
307 | position: static;
308 | }
309 | .aside {
310 | position: static;
311 | width: 100%;
312 | right: 0;
313 | }
314 |
315 | .hed {
316 | width: 100%;
317 | }
318 |
319 | .article {
320 | padding: 15px 0;
321 | }
322 |
323 | .article-header {
324 | width: 90vw;
325 | max-width: 600px;
326 | margin: 0 auto;
327 | }
328 | .article-body {
329 | width: 90vw;
330 | max-width: 600px;
331 | margin: 0 auto;
332 | padding-bottom: 80vh;
333 | }
334 |
335 | .fixed > div {
336 | width: 45%;
337 | }
338 |
339 | .fixed {
340 | position: fixed;
341 | left: 0;
342 | right: 0;
343 | bottom: 0;
344 | width: 100vw;
345 | top: initial;
346 | background: #EAE7D6;
347 | }
348 |
349 | .stats-container, .instructions, .logo-lockup {
350 | display: none;
351 | }
352 |
353 | }
354 |
355 |
--------------------------------------------------------------------------------