Prose
Styles for a block of article HTML (from markdown() or by hand): headings, lists, links, quotes, code, tables.
Demo
Writing a component
Each component is one file you copy into your program. It brings its own styles, follows the system's light or dark theme, and works with the keyboard. See the gallery for every one.
What goes in
Three things, in this order:
- A comment saying what it is, with an example.
- Its CSS, as
let name_css = css`...`. - The
vieworcomponent.
Views first: a component with nothing to remember is a view, built on what the browser already does.
Checking it
scripts/build.sh
QUILLO=target/release/quillo scripts/test.sh button| Kind | Runs | Script |
|---|---|---|
| view | on the server | none |
| component | server, then browser | its bundle |
- Colours only through the theme's variables
- so dark mode works by itself
- Press Tab to reach everything
Last edited today, yesterday.
Small, narrow
For a sidebar note, at 14px and 30 characters wide.
Large
For a lede or a landing page, at 18px.
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/prose.qweimport ui/theme.qwe use theme_cssuse ui/prose.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
view ProseDemo() = <div.demo-stack> <Prose id="prose-demo-article"> <h1>Writing a component</h1> <p>Each component is <strong>one file</strong> you copy into your program. It brings its own styles, follows the system's <em>light or dark</em> theme, and works with the keyboard. See <a href="#prose">the gallery</a> for every one.</p> <h2>What goes in</h2> <p>Three things, in this order:</p> <ol> <li>A comment saying what it is, with an example.</li> <li>Its CSS, as <code>let name_css = css`...`</code>.</li> <li>The <code>view</code> or <code>component</code>.</li> </ol> <blockquote><p>Views first: a component with nothing to remember is a view, built on what the browser already does.</p></blockquote> <h3>Checking it</h3> <pre><code>scripts/build.shQUILLO=target/release/quillo scripts/test.sh button</code></pre> <table> <thead><tr><th>Kind</th><th>Runs</th><th>Script</th></tr></thead> <tbody> <tr><td>view</td><td>on the server</td><td>none</td></tr> <tr><td>component</td><td>server, then browser</td><td>its bundle</td></tr> </tbody> </table> <ul> <li>Colours only through the theme's variables <ul><li>so dark mode works by itself</li></ul></li> <li>Press <kbd>Tab</kbd> to reach everything</li> </ul> <hr/> <p>Last edited <mark>today</mark>, <del>yesterday</del>.</p> </Prose> <div.demo-row> <Prose size="sm" width="30ch" id="prose-demo-small"><h2>Small, narrow</h2><p>For a sidebar note, at 14px and 30 characters wide.</p></Prose> <Prose size="lg" width="30ch" id="prose-demo-large"><h2>Large</h2><p>For a lede or a landing page, at 18px.</p></Prose> </div> </div>Source
// Prose: styles for a block of article HTML, from markdown() or written by hand:// headings, paragraphs, lists, links, quotes, code, tables, images and rules all// read well, in the theme, without a style of their own.//// <Prose>{markdown(post.body)}</Prose>// <Prose size="lg" width="60ch"><h1>Title</h1><p>...</p></Prose>//// children: the article's HTML. size: "sm", "md" (default) or "lg", the body text's size;// the rest scale with it. width: the measure, a CSS max-width ("68ch" by default;// null for none). id: the element's id, for a TableOfContents or a link.import ui/theme.qwe use ui_clslet prose_css = css`.ui-prose { max-width: 68ch; font: 16px/1.7 var(--ui-font); color: var(--ui-fg); overflow-wrap: anywhere; }.ui-prose-sm { font-size: 14px; line-height: 1.65; }.ui-prose-lg { font-size: 18px; line-height: 1.75; }.ui-prose > :first-child { margin-top: 0; }.ui-prose > :last-child { margin-bottom: 0; }.ui-prose p, .ui-prose ul, .ui-prose ol, .ui-prose blockquote, .ui-prose pre, .ui-prose table, .ui-prose figure,.ui-prose dl, .ui-prose details { margin: 0 0 1.1em; }.ui-prose h1, .ui-prose h2, .ui-prose h3, .ui-prose h4, .ui-prose h5, .ui-prose h6 { margin: 1.7em 0 .55em; font-weight: 650; line-height: 1.25; letter-spacing: -.015em; color: var(--ui-fg); }.ui-prose h1 { font-size: 2em; line-height: 1.15; letter-spacing: -.02em; }.ui-prose h2 { font-size: 1.5em; }.ui-prose h3 { font-size: 1.25em; }.ui-prose h4 { font-size: 1.1em; }.ui-prose h5 { font-size: 1em; }.ui-prose h6 { font-size: .85em; letter-spacing: .05em; text-transform: uppercase; color: var(--ui-muted); }.ui-prose :is(h1, h2, h3, h4, h5, h6) + :is(h1, h2, h3, h4, h5, h6) { margin-top: .4em; }.ui-prose a { color: var(--ui-accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }.ui-prose a:hover { text-decoration-thickness: 2px; }.ui-prose a:focus-visible { outline: none; border-radius: 3px; box-shadow: var(--ui-focus); }.ui-prose strong { font-weight: 600; }.ui-prose ul, .ui-prose ol { padding-left: 1.5em; }.ui-prose li { margin: .3em 0; }.ui-prose li > ul, .ui-prose li > ol { margin: .3em 0; }.ui-prose li::marker { color: var(--ui-muted); }.ui-prose ul:has(> li > input[type="checkbox"]) { padding-left: .25em; list-style: none; }.ui-prose li > input[type="checkbox"] { margin: 0 .5em 0 0; vertical-align: -.1em; }.ui-prose blockquote { margin-left: 0; padding: .25em 0 .25em 1.1em; border-left: 3px solid var(--ui-accent); color: var(--ui-muted); }.ui-prose blockquote > :last-child { margin-bottom: 0; }.ui-prose code { padding: .12em .35em; border-radius: 4px; background: var(--ui-subtle); font: .9em var(--ui-mono); }.ui-prose pre { padding: 14px 16px; overflow: auto; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-subtle); font: .875em/1.6 var(--ui-mono); tab-size: 4; }.ui-prose pre code { padding: 0; border-radius: 0; background: none; font: inherit; }.ui-prose kbd { padding: .1em .4em; border: 1px solid var(--ui-border); border-bottom-width: 2px; border-radius: 4px; background: var(--ui-surface); font: .85em var(--ui-mono); }.ui-prose mark { padding: 0 .15em; border-radius: 3px; background: color-mix(in srgb, var(--ui-warning) 28%, transparent); color: inherit; }.ui-prose table { width: 100%; border-collapse: collapse; font-size: .95em; line-height: 1.5; }.ui-prose th, .ui-prose td { padding: .5em .75em; border-bottom: 1px solid var(--ui-border); text-align: left; vertical-align: top; }.ui-prose th { font-weight: 600; border-bottom-color: var(--ui-strong); }.ui-prose tbody tr:last-child td { border-bottom-color: var(--ui-strong); }.ui-prose img, .ui-prose video { max-width: 100%; height: auto; border-radius: var(--ui-radius-sm); }.ui-prose figcaption { margin-top: .5em; font-size: .875em; color: var(--ui-muted); text-align: center; }.ui-prose hr { margin: 2em 0; border: 0; border-top: 1px solid var(--ui-border); }.ui-prose dt { font-weight: 600; }.ui-prose dd { margin: 0 0 .5em 1.5em; }.ui-prose del { color: var(--ui-muted); }.ui-prose sup, .ui-prose sub { font-size: .75em; line-height: 0; }`view Prose(size: str = "md", width: str? = "68ch", id: str? = null, class: str? = null, children) { style global prose_css <div class={ui_cls(["ui-prose", size != "md" && `ui-prose-${size}`, class])} id={id} style={width != "68ch" ? `max-width: ${width ?? "none"}` : null}>{children}</div>}Related
Thread 0 posts
On Prose: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.