├── .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 | --------------------------------------------------------------------------------