├── .nvmrc ├── docs └── CNAME ├── src ├── style │ ├── common │ │ ├── link-color.scss │ │ ├── heading-color.scss │ │ ├── body-font-family.scss │ │ ├── code-font-family.scss │ │ ├── media-queries │ │ │ ├── max-width-for-small-screens.scss │ │ │ ├── on-very-small-screens.scss │ │ │ ├── on-small-screens.scss │ │ │ └── on-regular-screens.scss │ │ ├── heading-selector.scss │ │ ├── heading-font-family.scss │ │ ├── heavy-shadow.scss │ │ ├── mild-shadow.scss │ │ ├── faux-inline-button.scss │ │ ├── default-example-user-input-shading.scss │ │ ├── example-user-input-shading.scss │ │ └── faux-button.scss │ ├── writing-conventions │ │ ├── image.scss │ │ ├── video.scss │ │ ├── parenthetical.scss │ │ ├── link.scss │ │ ├── emphasis-and-italics.scss │ │ ├── heavy-shadow.scss │ │ ├── footnote-block.scss │ │ ├── highlight.scss │ │ ├── footnote-reference.scss │ │ ├── line-block.scss │ │ ├── inline-quote.scss │ │ ├── thematic-break.scss │ │ ├── ordered-and-unordered-lists.scss │ │ ├── description-list.scss │ │ ├── blockquote.scss │ │ ├── revealable.scss │ │ ├── heading.scss │ │ ├── table.scss │ │ ├── example-user-input.scss │ │ └── code-blocks-and-inline-code.scss │ ├── editor.scss │ ├── app.scss │ ├── syncing-animation.scss │ ├── tab-list.scss │ ├── layout.scss │ ├── documentation-container.scss │ ├── table-of-contents.scss │ └── lib │ │ └── codemirror.css ├── getElementById.js ├── upSettings.js ├── repeat.js ├── isOnIosDevice.js ├── onLinkClick.js ├── debounce.js ├── isTotallyHidden.js ├── throttle.js ├── layout │ └── index.hbs ├── configureTableOfContentsVisibility.js ├── app.js ├── addScrollSyncingEventListeners.js ├── configureEditor.js └── content │ └── documentation.up ├── postcss.config.js ├── README.md ├── LICENSE ├── package.json └── webpack.config.js /.nvmrc: -------------------------------------------------------------------------------- 1 | v8.17.0 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | tryup.org 2 | -------------------------------------------------------------------------------- /src/style/common/link-color.scss: -------------------------------------------------------------------------------- 1 | $link-color: #579; 2 | -------------------------------------------------------------------------------- /src/style/common/heading-color.scss: -------------------------------------------------------------------------------- 1 | $heading-color: black; 2 | -------------------------------------------------------------------------------- /src/style/writing-conventions/image.scss: -------------------------------------------------------------------------------- 1 | img { max-width: 100% } 2 | -------------------------------------------------------------------------------- /src/style/writing-conventions/video.scss: -------------------------------------------------------------------------------- 1 | video { max-width: 100% } 2 | -------------------------------------------------------------------------------- /src/getElementById.js: -------------------------------------------------------------------------------- 1 | export default document.getElementById.bind(document) 2 | -------------------------------------------------------------------------------- /src/style/common/body-font-family.scss: -------------------------------------------------------------------------------- 1 | $body-font-family: system-ui, sans-serif; 2 | -------------------------------------------------------------------------------- /src/style/common/code-font-family.scss: -------------------------------------------------------------------------------- 1 | $code-font-family: Menlo, Consolas, monospace; 2 | -------------------------------------------------------------------------------- /src/style/common/media-queries/max-width-for-small-screens.scss: -------------------------------------------------------------------------------- 1 | $max-width-for-small-screens: 980px -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer') 4 | ] 5 | } -------------------------------------------------------------------------------- /src/style/common/heading-selector.scss: -------------------------------------------------------------------------------- 1 | $heading-selector: 'h1, h2, h3, h4, h5, h6, [role="heading"]'; 2 | -------------------------------------------------------------------------------- /src/style/common/heading-font-family.scss: -------------------------------------------------------------------------------- 1 | @import 'body-font-family'; 2 | 3 | $heading-font-family: $body-font-family; 4 | -------------------------------------------------------------------------------- /src/upSettings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parsing: { createSourceMap: true }, 3 | rendering: { idPrefix: '' } 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The [website](https://tryup.org) for [Up](https://github.com/start/up), a markup language for writing structured documents in plain text. 2 | -------------------------------------------------------------------------------- /src/style/common/heavy-shadow.scss: -------------------------------------------------------------------------------- 1 | @mixin heavy-shadow { 2 | box-shadow: 3 | 0 3px 6px rgba(0, 0, 0, 0.16), 4 | 0 3px 6px rgba(0, 0, 0, 0.23); 5 | } 6 | -------------------------------------------------------------------------------- /src/style/common/mild-shadow.scss: -------------------------------------------------------------------------------- 1 | @mixin mild-shadow { 2 | box-shadow: 3 | 0 1px 3px rgba(0, 0, 0, 0.12), 4 | 0 1px 2px rgba(0, 0, 0, 0.24); 5 | } 6 | -------------------------------------------------------------------------------- /src/style/writing-conventions/parenthetical.scss: -------------------------------------------------------------------------------- 1 | small { 2 | font-size: 100%; 3 | opacity: .5; 4 | } 5 | 6 | .up-square-brackets { font-style: italic } 7 | -------------------------------------------------------------------------------- /src/style/writing-conventions/link.scss: -------------------------------------------------------------------------------- 1 | @import '../common/link-color'; 2 | 3 | a { 4 | color: $link-color; 5 | 6 | &:hover { text-decoration: none; } 7 | } 8 | -------------------------------------------------------------------------------- /src/style/writing-conventions/emphasis-and-italics.scss: -------------------------------------------------------------------------------- 1 | i, em, caption { 2 | i, em { 3 | display: inline-block; 4 | transform: skewX(-20deg); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/style/common/media-queries/on-very-small-screens.scss: -------------------------------------------------------------------------------- 1 | @mixin on-very-small-screens { 2 | @media only screen and (max-width: 420px) { 3 | @content; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/style/writing-conventions/heavy-shadow.scss: -------------------------------------------------------------------------------- 1 | @mixin heavy-shadow { 2 | box-shadow: 3 | 0 3px 6px rgba(0, 0, 0, 0.16), 4 | 0 3px 6px rgba(0, 0, 0, 0.23); 5 | } 6 | -------------------------------------------------------------------------------- /src/repeat.js: -------------------------------------------------------------------------------- 1 | // Returns a new string consisting of `count` copies of `text`. 2 | export default function repeat(text, count) { 3 | return new Array(count + 1).join(text) 4 | } 5 | -------------------------------------------------------------------------------- /src/isOnIosDevice.js: -------------------------------------------------------------------------------- 1 | // Returns `true` on an iPhone, iPad, or iPod. 2 | export default function isOnIosDevice() { 3 | return ['iPhone', 'iPad', 'iPod'].indexOf(navigator.platform) !== -1 4 | } 5 | -------------------------------------------------------------------------------- /src/style/writing-conventions/footnote-block.scss: -------------------------------------------------------------------------------- 1 | .up-footnotes { 2 | font-size: 85%; 3 | 4 | dt { 5 | font-weight: normal; 6 | 7 | a:before { content: '↑' } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/style/common/media-queries/on-small-screens.scss: -------------------------------------------------------------------------------- 1 | @import 'max-width-for-small-screens'; 2 | 3 | @mixin on-small-screens { 4 | @media only screen and (max-width: $max-width-for-small-screens) { 5 | @content; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/style/writing-conventions/highlight.scss: -------------------------------------------------------------------------------- 1 | mark { 2 | background-color: yellow; 3 | padding: (1em / 16); 4 | 5 | mark { 6 | background-color: gold; 7 | 8 | mark { background-color: orange } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/style/common/media-queries/on-regular-screens.scss: -------------------------------------------------------------------------------- 1 | @import 'max-width-for-small-screens'; 2 | 3 | @mixin on-regular-screens { 4 | @media only screen and (min-width: 1 + $max-width-for-small-screens) { 5 | @content; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/style/writing-conventions/footnote-reference.scss: -------------------------------------------------------------------------------- 1 | .up-footnote-reference a { 2 | font-size: 110%; 3 | margin-left: (1em / 8); 4 | text-decoration: none; 5 | 6 | &:before { content: '[' } 7 | &:after { content: ']' } 8 | } 9 | -------------------------------------------------------------------------------- /src/style/common/faux-inline-button.scss: -------------------------------------------------------------------------------- 1 | @import 'faux-button'; 2 | 3 | @mixin faux-inline-button { 4 | @include faux-button; 5 | 6 | display: inline; 7 | font-size: 65%; 8 | margin: 0 5px; 9 | padding: 4px 12px 3px 12px; 10 | } 11 | -------------------------------------------------------------------------------- /src/style/editor.scss: -------------------------------------------------------------------------------- 1 | @import 'common/code-font-family'; 2 | 3 | .CodeMirror { 4 | font-size: 14px; 5 | line-height: 1.3; 6 | font-family: $code-font-family !important; 7 | 8 | .CodeMirror-scroll { -webkit-overflow-scrolling: touch } 9 | } 10 | -------------------------------------------------------------------------------- /src/style/writing-conventions/line-block.scss: -------------------------------------------------------------------------------- 1 | .up-lines { 2 | text-align: center; 3 | 4 | // Without some extra margin, consecutive line blocks appear merged together. 5 | + .up-lines { margin-top: 20px } 6 | 7 | > div { margin-bottom: 5px } 8 | } 9 | -------------------------------------------------------------------------------- /src/style/writing-conventions/inline-quote.scss: -------------------------------------------------------------------------------- 1 | q { 2 | margin: 0 (1em / 16); 3 | quotes: '“' '”' "‘" "’"; 4 | } 5 | 6 | q:before, 7 | q:after { font-family: serif } 8 | 9 | q:before { content: open-quote } 10 | q:after { content: close-quote } 11 | -------------------------------------------------------------------------------- /src/style/app.scss: -------------------------------------------------------------------------------- 1 | @import '~normalize.css'; 2 | @import 'layout'; 3 | @import 'editor'; 4 | @import 'tab-list'; 5 | @import 'documentation-container'; 6 | @import 'syncing-animation'; 7 | @import 'lib/codemirror'; 8 | 9 | html { scroll-behavior: smooth; } 10 | -------------------------------------------------------------------------------- /src/style/syncing-animation.scss: -------------------------------------------------------------------------------- 1 | @mixin dirty { 2 | opacity: 0.5; 3 | transition: 0.5s; 4 | } 5 | 6 | @keyframes clean { 7 | from { @include dirty } 8 | to { opacity: 1 } 9 | } 10 | 11 | .dirty, { @include dirty } 12 | .clean { animation: 0.2s clean } 13 | -------------------------------------------------------------------------------- /src/style/tab-list.scss: -------------------------------------------------------------------------------- 1 | @import 'common/faux-button'; 2 | 3 | #tab-container { 4 | display: flex; 5 | justify-content: center; 6 | 7 | span[role="tab"] { 8 | @include faux-button; 9 | 10 | &[aria-selected="true"] { display: none } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/style/common/default-example-user-input-shading.scss: -------------------------------------------------------------------------------- 1 | @import 'example-user-input-shading'; 2 | 3 | @mixin default-example-user-input-shading { 4 | @include example-user-input-shading( 5 | $text-color: #444, 6 | $border-color: #aaa, 7 | $shadow-color: rgba(#0d0d0d, 0.2)); 8 | } 9 | -------------------------------------------------------------------------------- /src/style/writing-conventions/thematic-break.scss: -------------------------------------------------------------------------------- 1 | hr { 2 | border: 0; 3 | padding-bottom: 30px; 4 | text-align: center; 5 | } 6 | 7 | hr:after { 8 | color: #ccc; 9 | content: '☆ ☆ ☆'; 10 | font-size: 10px; 11 | } 12 | 13 | @media speech, braille { 14 | hr:after { content: none } 15 | } 16 | -------------------------------------------------------------------------------- /src/onLinkClick.js: -------------------------------------------------------------------------------- 1 | export default function onLinkClick(container, callback) { 2 | container.addEventListener('click', event => { 3 | let clickedElement = event.target 4 | 5 | do { 6 | if (clickedElement.tagName === 'A') { 7 | callback(event) 8 | return 9 | } 10 | } while (clickedElement = clickedElement.parentNode) 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /src/debounce.js: -------------------------------------------------------------------------------- 1 | // Returns a version of `callback` that won't execute until `delay` 2 | // milliseconds after it was most recently invoked. 3 | export default function debounce(callback, delay) { 4 | let timeoutHandle 5 | 6 | return (...args) => { 7 | clearTimeout(timeoutHandle) 8 | 9 | timeoutHandle = 10 | setTimeout(() => callback(...args), delay) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/style/writing-conventions/ordered-and-unordered-lists.scss: -------------------------------------------------------------------------------- 1 | // Without some extra margin, consecutive lists appear merged together. 2 | ol + ol, 3 | ul + ul { margin-top: 40px } 4 | 5 | // Most list items contain just a single line (technically a paragraph). 6 | // 7 | // Unless we shrink the margins of that single inner paragraph, those 8 | // list items appear a little too spaced out. 9 | li p:only-child { margin: .5em 0 } -------------------------------------------------------------------------------- /src/style/common/example-user-input-shading.scss: -------------------------------------------------------------------------------- 1 | @mixin example-user-input-shading($text-color, $border-color, $shadow-color) { 2 | $bottom-edge-length: (1em / 4); 3 | $side-edge-length: (1em / 8); 4 | 5 | border-color: $border-color; 6 | box-shadow: 7 | 0 $bottom-edge-length 0 $shadow-color, 8 | $side-edge-length $bottom-edge-length 0 $shadow-color, 9 | -$side-edge-length $bottom-edge-length 0 $shadow-color; 10 | color: $text-color; 11 | } 12 | -------------------------------------------------------------------------------- /src/style/writing-conventions/description-list.scss: -------------------------------------------------------------------------------- 1 | @import '../common/heading-color'; 2 | @import '../common/heading-font-family'; 3 | 4 | dl { 5 | margin-bottom: 25px; 6 | 7 | dl { margin-bottom: 0 } 8 | 9 | dt { 10 | color: $heading-color; 11 | font-family: $heading-font-family; 12 | font-weight: bold; 13 | } 14 | 15 | dd { margin-left: 20px } 16 | 17 | // Without some extra spacing, consecutive description lists appear merged together. 18 | + dl { margin-top: 45px } 19 | } 20 | -------------------------------------------------------------------------------- /src/style/common/faux-button.scss: -------------------------------------------------------------------------------- 1 | @import 'mild-shadow'; 2 | 3 | @mixin faux-button { 4 | @include mild-shadow; 5 | 6 | background-color: #c00; 7 | border-radius: 3px; 8 | color: white; 9 | cursor: pointer; 10 | display: inline-block; 11 | font-size: 75%; 12 | margin: 10px 0; 13 | padding: 7px 12px 5px 12px; 14 | text-transform: uppercase; 15 | white-space: nowrap; 16 | user-select: none; 17 | 18 | &:hover { 19 | background: #b00; 20 | } 21 | 22 | &:active { 23 | background: #a00; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/isTotallyHidden.js: -------------------------------------------------------------------------------- 1 | // Returns `true` if an element is hidden (directly or indirectly) via 2 | // `display: none`. 3 | // 4 | // NOTE: This function does not work for elements whose `position` is `fixed`; it 5 | // will always return `true`. 6 | export default function isTotallyHidden(element) { 7 | // If an element's `display` (or the `display` of an ancestor) is set to `none`, 8 | // its `offsetParent` returns `null`. 9 | // 10 | // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent 11 | // 12 | // We don't check `style.display` directly, because it returns an empty string if 13 | // it wasn't set by JavaScript. 14 | return !element.offsetParent; 15 | } 16 | -------------------------------------------------------------------------------- /src/style/writing-conventions/blockquote.scss: -------------------------------------------------------------------------------- 1 | @mixin nested-blockquoted-colors($nesting-level) { 2 | $amount-to-darken: $nesting-level * 4%; 3 | 4 | border-color: darken($blockquote-indent-color, $amount-to-darken); 5 | background-color: darken($blockquote-bg-color, $amount-to-darken); 6 | } 7 | 8 | $blockquote-bg-color: #fcfcfc; 9 | $blockquote-indent-color: #ddd; 10 | 11 | blockquote { 12 | border-left: 20px solid $blockquote-indent-color; 13 | background-color: $blockquote-bg-color; 14 | margin: 15px 0; 15 | padding: 5px 20px; 16 | 17 | blockquote { 18 | @include nested-blockquoted-colors(1); 19 | 20 | blockquote { 21 | @include nested-blockquoted-colors(2); 22 | 23 | blockquote { 24 | @include nested-blockquoted-colors(3) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/style/layout.scss: -------------------------------------------------------------------------------- 1 | @import 'common/media-queries/on-regular-screens'; 2 | @import 'common/media-queries/on-small-screens'; 3 | 4 | 5 | #tab-container { 6 | align-items: center; 7 | display: flex; 8 | height: 44px; 9 | } 10 | 11 | $editor-width: 510px; 12 | 13 | #documentation-container { 14 | margin-left: $editor-width; 15 | padding: 0 15px; 16 | 17 | #table-of-contents { display: none } 18 | 19 | #documentation, 20 | #table-of-contents { 21 | margin: 0 auto; 22 | max-width: 580px; 23 | overflow-x: hidden; 24 | } 25 | } 26 | 27 | @include on-regular-screens { 28 | #editor-container { 29 | border-right: 1px solid #aaa; 30 | position: fixed; 31 | left: 0; 32 | top: 0; 33 | width: $editor-width; 34 | 35 | .CodeMirror { height: 100vh; } 36 | } 37 | } 38 | 39 | @include on-small-screens { 40 | #editor-container { display: none } 41 | #documentation-container { margin-left: 0 } 42 | } 43 | -------------------------------------------------------------------------------- /src/style/writing-conventions/revealable.scss: -------------------------------------------------------------------------------- 1 | @import 'common/faux-button'; 2 | @import 'common/faux-inline-button'; 3 | 4 | @keyframes reveal { 5 | 0% { opacity: 1; } 6 | 30% { opacity: .5 } 7 | 100% { opacity: 1 } 8 | } 9 | 10 | // Inline revealables and revealable blocks 11 | .up-revealable { 12 | > label { @include faux-button } 13 | 14 | > input[type="radio"] { 15 | display: none; 16 | 17 | ~ span, 18 | ~ div { display: none } 19 | 20 | &.up-hide:checked + label { display: none } 21 | 22 | &.up-reveal:checked { 23 | + label { display: none } 24 | 25 | ~ span, 26 | ~ div { animation: 1s reveal } 27 | 28 | ~ span { display: inline } 29 | ~ div { display: block } 30 | } 31 | } 32 | 33 | > div { margin-left: 20px } 34 | } 35 | 36 | // Inline revealables 37 | span.up-revealable > label { @include faux-inline-button } 38 | 39 | // Revealable blocks 40 | div.up-revealable > label { @include faux-button } 41 | -------------------------------------------------------------------------------- /src/style/writing-conventions/heading.scss: -------------------------------------------------------------------------------- 1 | @import '../common/heading-selector'; 2 | @import '../common/heading-color'; 3 | @import '../common/heading-font-family'; 4 | 5 | 6 | #{$heading-selector} { 7 | color: $heading-color; 8 | font-family: $heading-font-family; 9 | line-height: 1.5; 10 | overflow-x: auto; 11 | padding-top: 10px; 12 | } 13 | 14 | h1 { 15 | font-size: 180%; 16 | padding-bottom: 15px; 17 | text-align: center; 18 | } 19 | 20 | h2 { font-size: 140% } 21 | h3 { font-size: 120% } 22 | h4 { font-size: 110% } 23 | h5 { font-size: 100% } 24 | h6 { font-size: 90% } 25 | 26 | // Headings with levels 7 and higher are rendered as `