dowel-ui 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/dist/index.d.ts +34 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -1
- package/dist/prose.css +448 -0
- package/dist/registry.json +373 -55
- package/dist/theme.css +257 -0
- package/dist/tokens.json +233 -1
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ A dowel is the hidden peg that joins two boards so the seam does not show. That
|
|
|
11
11
|
**[Documentation](https://lacodda.github.io/dowel/)** — what everything is and why it is that way.
|
|
12
12
|
**[The stand](https://lacodda.github.io/dowel/stand/)** — every component, live, in either theme and in the accent of any product of the line.
|
|
13
13
|
|
|
14
|
-
**Status:** v0.
|
|
14
|
+
**Status:** v0.25.0 - the theme, the scales, an accent per product, seventy-six components - overlays, menus, the command palette, the table, a hundred thousand rows, the screens between asking for data and showing it, the three ways of saying something happened, charts, and now text and code: rendered markdown through one stylesheet, a code block whose highlighter is yours, a comparison whose two columns stay in step, and a JSON document read rather than parsed by eye - and the gates each one passes: axe, the keyboard, a dependency budget and a picture in both themes. Every component has a page of its own on the stand, which remembers the theme and the accent you left it in. Components install from a versioned registry, the docs are served in the form an agent reads, and `dowel check` tells a project what stands between it and the vocabulary. Two products of the line live on it. See the [roadmap](#roadmap).
|
|
15
15
|
|
|
16
16
|
## The theme
|
|
17
17
|
|
|
@@ -68,10 +68,21 @@ A product of the line states one thing about its appearance - which product it i
|
|
|
68
68
|
@import 'dowel-ui/accents/kilna.css';
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
Text a product does not write by hand - rendered markdown, a description from a CMS, a model's reply - is the one thing a component cannot style, because the tags arrive already made. That is a second stylesheet, imported the same way and applied with one class:
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
@import 'dowel-ui/prose.css';
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<div className="prose" dangerouslySetInnerHTML={{ __html: sanitised }} />
|
|
79
|
+
```
|
|
80
|
+
|
|
71
81
|
Or copy the files in instead of depending on the package:
|
|
72
82
|
|
|
73
83
|
```bash
|
|
74
84
|
npx shadcn@latest add https://lacodda.github.io/dowel/r/theme.json
|
|
85
|
+
npx shadcn@latest add https://lacodda.github.io/dowel/r/prose.json
|
|
75
86
|
```
|
|
76
87
|
|
|
77
88
|
## Primitives
|
|
@@ -93,12 +104,12 @@ npx shadcn@latest add https://lacodda.github.io/dowel/r/app.json
|
|
|
93
104
|
A set carries no files of its own: it resolves into the same per-component
|
|
94
105
|
installs you could have typed, so nothing of it survives in your project and
|
|
95
106
|
there is no membership to leave. Each minor of the registry is also served
|
|
96
|
-
frozen at `r/v0.
|
|
107
|
+
frozen at `r/v0.25/…`, for an install that has to be repeatable - inside a
|
|
97
108
|
snapshot the cross-references point into the same snapshot, so a component and
|
|
98
109
|
the sibling it reuses are the pair that shipped together. See
|
|
99
110
|
[installing from the registry](https://lacodda.github.io/dowel/guides/registry/).
|
|
100
111
|
|
|
101
|
-
|
|
112
|
+
Seventy-six of them so far. The everyday ones - Button, Input, Textarea, Panel,
|
|
102
113
|
Badge, Chip, Kbd, Spinner, Truncate and Copyable; Field and the three controls
|
|
103
114
|
that answer a question - Checkbox, RadioGroup and Switch; five for a number or
|
|
104
115
|
a judgement - NumberField, Slider, RatingScale, DurationField and
|
|
@@ -112,8 +123,16 @@ them; three for saying that something happened - Toast, Alert and Banner; six
|
|
|
112
123
|
for showing rows of data - Table, Pagination, PageSize, NumberFormat,
|
|
113
124
|
RelativeTime and the `table-sort` with no React in it; four for a long list or
|
|
114
125
|
a deep one - VirtualList, TreeView with the `tree-rows` under it, and KeyValue;
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
StatTile, for one figure and what it is a figure of, with Sparkline for the
|
|
127
|
+
shape of its history, and Track with its `track-segments` for a bar divided
|
|
128
|
+
into stretches; ActivityHeatmap with its legend and its `activity-weeks`, for a
|
|
129
|
+
year of days; BarChart for a period at a time, LineChart with its `line-scale` for a level
|
|
130
|
+
between the readings; and five for the screens
|
|
131
|
+
between asking for data and showing it: Skeleton, EmptyState, Progress,
|
|
132
|
+
QueryState and ErrorBoundary. And six for text and code: the `prose`
|
|
133
|
+
stylesheet for markdown a product did not write by hand, CodeBlock with
|
|
134
|
+
CopyButton beside it, DiffView with the `diff-lines` that keeps its two
|
|
135
|
+
columns in step, and JsonViewer over its `json-rows`.
|
|
117
136
|
|
|
118
137
|
Those last three are the ones products keep confusing, so each page names all
|
|
119
138
|
four options: a **toast** goes away, an **alert** is still true after a reload,
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,33 @@ export { defaultStorageKey, initTheme, nextTheme, resolvedTheme, useTheme, useTh
|
|
|
4
4
|
/** Tokens that carry a colour. Ordered as they read on a screen: grounds,
|
|
5
5
|
* then hairlines, then ink, then the accent, then status. */
|
|
6
6
|
export declare const colorTokens: readonly ["bg", "raise", "soft", "softer", "line", "line-2", "text", "dim", "faint", "accent", "accent-2", "accent-soft", "on-accent", "on-good", "on-warn", "on-bad", "on-info", "good", "good-soft", "warn", "warn-soft", "bad", "bad-soft", "info", "info-soft"];
|
|
7
|
+
/** Series colours, for charts: identity, assigned 1..8 in order and never
|
|
8
|
+
* cycled. Alone in this vocabulary alongside the status hues, they do not
|
|
9
|
+
* follow the product accent - a series belongs to the data, not to the
|
|
10
|
+
* product drawing it. The order is load-bearing: it is what keeps adjacent
|
|
11
|
+
* pairs apart under colour-blind simulation, so these are not re-sorted. */
|
|
12
|
+
export declare const seriesTokens: readonly ["series-1", "series-2", "series-3", "series-4", "series-5", "series-6", "series-7", "series-8"];
|
|
13
|
+
/** Magnitude: one hue from light to dark, for heatmap cells and anything else
|
|
14
|
+
* that encodes "how much" rather than "which". */
|
|
15
|
+
export declare const scaleTokens: readonly ["scale-100", "scale-200", "scale-300", "scale-400", "scale-500", "scale-600", "scale-700"];
|
|
16
|
+
/** A chart's own furniture, quieter than `line` because a gridline that
|
|
17
|
+
* competes with the data is drawn wrong. */
|
|
18
|
+
export declare const chartTokens: readonly ["chart-grid", "chart-axis"];
|
|
19
|
+
/** Heat: five discrete steps for a grid of cells where colour carries the
|
|
20
|
+
* value. Ordinal rather than sequential - told apart at a glance and matched
|
|
21
|
+
* against a legend - which is why these are their own ramp and not five of
|
|
22
|
+
* `scaleTokens`. The faintest step stays distinct from an empty cell, or the
|
|
23
|
+
* grid claims a day was worked at zero when nobody reported it. */
|
|
24
|
+
export declare const heatTokens: readonly ["heat-1", "heat-2", "heat-3", "heat-4", "heat-5"];
|
|
25
|
+
/** Syntax: the eight kinds of thing in a piece of code that are worth telling
|
|
26
|
+
* apart in every language. Fixed like the series - `if` should not be magenta
|
|
27
|
+
* in one product and cobalt in another - but measured against a different
|
|
28
|
+
* threshold: these are read as text, so every slot clears 4.5:1 against the
|
|
29
|
+
* surface code sits on, where a series colour only has to clear 3:1 as a
|
|
30
|
+
* filled mark. They are also the one palette here not held to the
|
|
31
|
+
* colour-blind floor, because syntax colour restates what the text already
|
|
32
|
+
* says and CodeBlock's default draws none of it. */
|
|
33
|
+
export declare const syntaxTokens: readonly ["syntax-keyword", "syntax-string", "syntax-number", "syntax-comment", "syntax-name", "syntax-type", "syntax-punctuation", "syntax-meta"];
|
|
7
34
|
/** Tokens a product overrides to make the theme its own: the accent from the
|
|
8
35
|
* brand-line registry, and how much of it bleeds into the greys. */
|
|
9
36
|
export declare const themeParameters: readonly ["accent-base", "neutral-base", "neutral-tint", "neutral-tint-strong", "ground", "ink"];
|
|
@@ -28,7 +55,7 @@ export declare const layerTokens: readonly ["z-popup", "z-sticky", "z-menu", "z-
|
|
|
28
55
|
* vocabulary - a docs page, an inspector, the JSON export, the test that keeps
|
|
29
56
|
* this file honest against the stylesheet - reads this, so a new category
|
|
30
57
|
* cannot be added and quietly missed by half of them. */
|
|
31
|
-
export declare const allTokens: readonly ["bg", "raise", "soft", "softer", "line", "line-2", "text", "dim", "faint", "accent", "accent-2", "accent-soft", "on-accent", "on-good", "on-warn", "on-bad", "on-info", "good", "good-soft", "warn", "warn-soft", "bad", "bad-soft", "info", "info-soft", "accent-base", "neutral-base", "neutral-tint", "neutral-tint-strong", "ground", "ink", "shadow-lift", "shadow-raise", "shadow-float", "radius-xs", "radius-sm", "radius-md", "radius-lg", "radius-xl", "radius-2xl", "radius-inner", "font-sans", "font-mono", "text-2xs", "text-xs", "text-sm", "text-base", "text-lg", "text-xl", "text-2xl", "font-weight-normal", "font-weight-medium", "font-weight-semibold", "tracking-caption", "tracking-tight", "duration-quick", "duration-base", "duration-slow", "ease-out", "ease-in-out", "z-popup", "z-sticky", "z-menu", "z-floating", "z-overlay", "z-modal", "z-palette", "z-toast"];
|
|
58
|
+
export declare const allTokens: readonly ["bg", "raise", "soft", "softer", "line", "line-2", "text", "dim", "faint", "accent", "accent-2", "accent-soft", "on-accent", "on-good", "on-warn", "on-bad", "on-info", "good", "good-soft", "warn", "warn-soft", "bad", "bad-soft", "info", "info-soft", "series-1", "series-2", "series-3", "series-4", "series-5", "series-6", "series-7", "series-8", "scale-100", "scale-200", "scale-300", "scale-400", "scale-500", "scale-600", "scale-700", "chart-grid", "chart-axis", "heat-1", "heat-2", "heat-3", "heat-4", "heat-5", "syntax-keyword", "syntax-string", "syntax-number", "syntax-comment", "syntax-name", "syntax-type", "syntax-punctuation", "syntax-meta", "accent-base", "neutral-base", "neutral-tint", "neutral-tint-strong", "ground", "ink", "shadow-lift", "shadow-raise", "shadow-float", "radius-xs", "radius-sm", "radius-md", "radius-lg", "radius-xl", "radius-2xl", "radius-inner", "font-sans", "font-mono", "text-2xs", "text-xs", "text-sm", "text-base", "text-lg", "text-xl", "text-2xl", "font-weight-normal", "font-weight-medium", "font-weight-semibold", "tracking-caption", "tracking-tight", "duration-quick", "duration-base", "duration-slow", "ease-out", "ease-in-out", "z-popup", "z-sticky", "z-menu", "z-floating", "z-overlay", "z-modal", "z-palette", "z-toast"];
|
|
32
59
|
export type ColorToken = (typeof colorTokens)[number];
|
|
33
60
|
export type ThemeParameter = (typeof themeParameters)[number];
|
|
34
61
|
export type ElevationToken = (typeof elevationTokens)[number];
|
|
@@ -36,7 +63,12 @@ export type RadiusToken = (typeof radiusTokens)[number];
|
|
|
36
63
|
export type TypeToken = (typeof typeTokens)[number];
|
|
37
64
|
export type MotionToken = (typeof motionTokens)[number];
|
|
38
65
|
export type LayerToken = (typeof layerTokens)[number];
|
|
39
|
-
export type
|
|
66
|
+
export type SeriesToken = (typeof seriesTokens)[number];
|
|
67
|
+
export type ScaleToken = (typeof scaleTokens)[number];
|
|
68
|
+
export type ChartToken = (typeof chartTokens)[number];
|
|
69
|
+
export type HeatToken = (typeof heatTokens)[number];
|
|
70
|
+
export type SyntaxToken = (typeof syntaxTokens)[number];
|
|
71
|
+
export type Token = ColorToken | SeriesToken | ScaleToken | ChartToken | HeatToken | SyntaxToken | ThemeParameter | ElevationToken | RadiusToken | TypeToken | MotionToken | LayerToken;
|
|
40
72
|
/** The custom property a token is read from: `token('accent')` is
|
|
41
73
|
* `'--accent'`. Spelled out here so that no caller builds the string itself
|
|
42
74
|
* and gets the prefix subtly wrong. */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACvE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,QAAQ,EACR,cAAc,EACd,KAAK,KAAK,GACX,MAAM,YAAY,CAAA;AAEnB;6DAC6D;AAC7D,eAAO,MAAM,WAAW,qQA0Bd,CAAA;AAEV;oEACoE;AACpE,eAAO,MAAM,eAAe,kGAOlB,CAAA;AAEV;yDACyD;AACzD,eAAO,MAAM,eAAe,0DAA2D,CAAA;AAEvF;8DAC8D;AAC9D,eAAO,MAAM,YAAY,0GAQf,CAAA;AAEV;iCACiC;AACjC,eAAO,MAAM,UAAU,gOAeb,CAAA;AAEV;;0CAE0C;AAC1C,eAAO,MAAM,YAAY,0FAMf,CAAA;AAEV;;qEAEqE;AACrE,eAAO,MAAM,WAAW,0GASd,CAAA;AAEV;;;yDAGyD;AACzD,eAAO,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACvE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,QAAQ,EACR,cAAc,EACd,KAAK,KAAK,GACX,MAAM,YAAY,CAAA;AAEnB;6DAC6D;AAC7D,eAAO,MAAM,WAAW,qQA0Bd,CAAA;AAEV;;;;4EAI4E;AAC5E,eAAO,MAAM,YAAY,2GASf,CAAA;AAEV;kDACkD;AAClD,eAAO,MAAM,WAAW,sGAQd,CAAA;AAEV;4CAC4C;AAC5C,eAAO,MAAM,WAAW,uCAAwC,CAAA;AAEhE;;;;mEAImE;AACnE,eAAO,MAAM,UAAU,6DAA8D,CAAA;AAErF;;;;;;;oDAOoD;AACpD,eAAO,MAAM,YAAY,oJASf,CAAA;AAEV;oEACoE;AACpE,eAAO,MAAM,eAAe,kGAOlB,CAAA;AAEV;yDACyD;AACzD,eAAO,MAAM,eAAe,0DAA2D,CAAA;AAEvF;8DAC8D;AAC9D,eAAO,MAAM,YAAY,0GAQf,CAAA;AAEV;iCACiC;AACjC,eAAO,MAAM,UAAU,gOAeb,CAAA;AAEV;;0CAE0C;AAC1C,eAAO,MAAM,YAAY,0FAMf,CAAA;AAEV;;qEAEqE;AACrE,eAAO,MAAM,WAAW,0GASd,CAAA;AAEV;;;yDAGyD;AACzD,eAAO,MAAM,SAAS,+vCAaZ,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAC7D,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AACvD,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AACnD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AACvD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AAKrD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AACvD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AACnD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvD,MAAM,MAAM,KAAK,GACb,UAAU,GACV,WAAW,GACX,UAAU,GACV,UAAU,GACV,SAAS,GACT,WAAW,GACX,cAAc,GACd,cAAc,GACd,WAAW,GACX,SAAS,GACT,WAAW,GACX,UAAU,CAAA;AAEd;;uCAEuC;AACvC,wBAAgB,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAEzC"}
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,59 @@ export const colorTokens = [
|
|
|
46
46
|
'info',
|
|
47
47
|
'info-soft',
|
|
48
48
|
];
|
|
49
|
+
/** Series colours, for charts: identity, assigned 1..8 in order and never
|
|
50
|
+
* cycled. Alone in this vocabulary alongside the status hues, they do not
|
|
51
|
+
* follow the product accent - a series belongs to the data, not to the
|
|
52
|
+
* product drawing it. The order is load-bearing: it is what keeps adjacent
|
|
53
|
+
* pairs apart under colour-blind simulation, so these are not re-sorted. */
|
|
54
|
+
export const seriesTokens = [
|
|
55
|
+
'series-1',
|
|
56
|
+
'series-2',
|
|
57
|
+
'series-3',
|
|
58
|
+
'series-4',
|
|
59
|
+
'series-5',
|
|
60
|
+
'series-6',
|
|
61
|
+
'series-7',
|
|
62
|
+
'series-8',
|
|
63
|
+
];
|
|
64
|
+
/** Magnitude: one hue from light to dark, for heatmap cells and anything else
|
|
65
|
+
* that encodes "how much" rather than "which". */
|
|
66
|
+
export const scaleTokens = [
|
|
67
|
+
'scale-100',
|
|
68
|
+
'scale-200',
|
|
69
|
+
'scale-300',
|
|
70
|
+
'scale-400',
|
|
71
|
+
'scale-500',
|
|
72
|
+
'scale-600',
|
|
73
|
+
'scale-700',
|
|
74
|
+
];
|
|
75
|
+
/** A chart's own furniture, quieter than `line` because a gridline that
|
|
76
|
+
* competes with the data is drawn wrong. */
|
|
77
|
+
export const chartTokens = ['chart-grid', 'chart-axis'];
|
|
78
|
+
/** Heat: five discrete steps for a grid of cells where colour carries the
|
|
79
|
+
* value. Ordinal rather than sequential - told apart at a glance and matched
|
|
80
|
+
* against a legend - which is why these are their own ramp and not five of
|
|
81
|
+
* `scaleTokens`. The faintest step stays distinct from an empty cell, or the
|
|
82
|
+
* grid claims a day was worked at zero when nobody reported it. */
|
|
83
|
+
export const heatTokens = ['heat-1', 'heat-2', 'heat-3', 'heat-4', 'heat-5'];
|
|
84
|
+
/** Syntax: the eight kinds of thing in a piece of code that are worth telling
|
|
85
|
+
* apart in every language. Fixed like the series - `if` should not be magenta
|
|
86
|
+
* in one product and cobalt in another - but measured against a different
|
|
87
|
+
* threshold: these are read as text, so every slot clears 4.5:1 against the
|
|
88
|
+
* surface code sits on, where a series colour only has to clear 3:1 as a
|
|
89
|
+
* filled mark. They are also the one palette here not held to the
|
|
90
|
+
* colour-blind floor, because syntax colour restates what the text already
|
|
91
|
+
* says and CodeBlock's default draws none of it. */
|
|
92
|
+
export const syntaxTokens = [
|
|
93
|
+
'syntax-keyword',
|
|
94
|
+
'syntax-string',
|
|
95
|
+
'syntax-number',
|
|
96
|
+
'syntax-comment',
|
|
97
|
+
'syntax-name',
|
|
98
|
+
'syntax-type',
|
|
99
|
+
'syntax-punctuation',
|
|
100
|
+
'syntax-meta',
|
|
101
|
+
];
|
|
49
102
|
/** Tokens a product overrides to make the theme its own: the accent from the
|
|
50
103
|
* brand-line registry, and how much of it bleeds into the greys. */
|
|
51
104
|
export const themeParameters = [
|
|
@@ -117,6 +170,11 @@ export const layerTokens = [
|
|
|
117
170
|
* cannot be added and quietly missed by half of them. */
|
|
118
171
|
export const allTokens = [
|
|
119
172
|
...colorTokens,
|
|
173
|
+
...seriesTokens,
|
|
174
|
+
...scaleTokens,
|
|
175
|
+
...chartTokens,
|
|
176
|
+
...heatTokens,
|
|
177
|
+
...syntaxTokens,
|
|
120
178
|
...themeParameters,
|
|
121
179
|
...elevationTokens,
|
|
122
180
|
...radiusTokens,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAoB,MAAM,WAAW,CAAA;AACvE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,QAAQ,EACR,cAAc,GAEf,MAAM,YAAY,CAAA;AAEnB;6DAC6D;AAC7D,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI;IACJ,OAAO;IACP,MAAM;IACN,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,UAAU;IACV,aAAa;IACb,WAAW;IACX,SAAS;IACT,SAAS;IACT,QAAQ;IACR,SAAS;IACT,MAAM;IACN,WAAW;IACX,MAAM;IACN,WAAW;IACX,KAAK;IACL,UAAU;IACV,MAAM;IACN,WAAW;CACH,CAAA;AAEV;oEACoE;AACpE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa;IACb,cAAc;IACd,cAAc;IACd,qBAAqB;IACrB,QAAQ;IACR,KAAK;CACG,CAAA;AAEV;yDACyD;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,cAAc,CAAU,CAAA;AAEvF;8DAC8D;AAC9D,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,WAAW;IACX,WAAW;IACX,WAAW;IACX,WAAW;IACX,WAAW;IACX,YAAY;IACZ,cAAc;CACN,CAAA;AAEV;iCACiC;AACjC,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,WAAW;IACX,WAAW;IACX,UAAU;IACV,SAAS;IACT,SAAS;IACT,WAAW;IACX,SAAS;IACT,SAAS;IACT,UAAU;IACV,oBAAoB;IACpB,oBAAoB;IACpB,sBAAsB;IACtB,kBAAkB;IAClB,gBAAgB;CACR,CAAA;AAEV;;0CAE0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,gBAAgB;IAChB,eAAe;IACf,eAAe;IACf,UAAU;IACV,aAAa;CACL,CAAA;AAEV;;qEAEqE;AACrE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,SAAS;IACT,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,WAAW;IACX,SAAS;IACT,WAAW;IACX,SAAS;CACD,CAAA;AAEV;;;yDAGyD;AACzD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,GAAG,WAAW;IACd,GAAG,eAAe;IAClB,GAAG,eAAe;IAClB,GAAG,YAAY;IACf,GAAG,UAAU;IACb,GAAG,YAAY;IACf,GAAG,WAAW;CACN,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/prose.css
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* dowel prose - the shape of text a product did not write by hand.
|
|
3
|
+
*
|
|
4
|
+
* Every other rule in this system is applied by a component, because a
|
|
5
|
+
* component owns the element it draws. Rendered markdown is the case where
|
|
6
|
+
* that is impossible: the product hands the DOM a string of HTML - from
|
|
7
|
+
* `marked`, from a CMS, from a model's reply - and there is no React element
|
|
8
|
+
* to hang a class on. The tags arrive already made. Only a descendant selector
|
|
9
|
+
* reaches them.
|
|
10
|
+
*
|
|
11
|
+
* kilna proved the shape before this file existed, as thirty `[&_h1]:mt-4`
|
|
12
|
+
* arbitrary variants inside one `className` string. It works and it is a
|
|
13
|
+
* paragraph of unreadable text that no second product can share, which is the
|
|
14
|
+
* whole argument for moving it here.
|
|
15
|
+
*
|
|
16
|
+
* Scoped to `.prose`, so nothing leaks: a stylesheet that styled `h2`
|
|
17
|
+
* globally would reach into every component that happens to render one.
|
|
18
|
+
*
|
|
19
|
+
* WHY NOT A TYPOGRAPHY PLUGIN. `@tailwindcss/typography` answers the same
|
|
20
|
+
* question and brings its own answer to a different one - its own type scale,
|
|
21
|
+
* its own greys, its own idea of measure. Installing it next to this theme
|
|
22
|
+
* means two vocabularies describing the same text, and the one that wins is
|
|
23
|
+
* whichever loaded last. Every value below is a token from `theme.css`; there
|
|
24
|
+
* is not a single colour or size written down here.
|
|
25
|
+
*
|
|
26
|
+
* WHAT THIS IS NOT FOR. Interface text - a label, a row, a button - is styled
|
|
27
|
+
* by the component that draws it. `.prose` is for a reading column: a note, a
|
|
28
|
+
* description, an article, a chat reply. Wrapping a form in it is how a screen
|
|
29
|
+
* ends up with two competing ideas of what `text-sm` means.
|
|
30
|
+
*
|
|
31
|
+
* Import after the theme:
|
|
32
|
+
*
|
|
33
|
+
* @import 'tailwindcss';
|
|
34
|
+
* @import './dowel/theme.css';
|
|
35
|
+
* @import './dowel/prose.css';
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
.prose {
|
|
39
|
+
/*
|
|
40
|
+
* The reading size is `--text-base`, not the `--text-sm` the interface runs
|
|
41
|
+
* at, and the two are different on purpose. Chrome is scanned - a label is
|
|
42
|
+
* recognised rather than read - and it packs tighter the less of it there
|
|
43
|
+
* is. Prose is read word by word, and 12px of continuous text is where a
|
|
44
|
+
* reader starts leaning in. The products already knew this and did it by
|
|
45
|
+
* hand: kilna's rendered markdown sets `text-sm` (12px) against an interface
|
|
46
|
+
* of 11px, the same one-step lift.
|
|
47
|
+
*/
|
|
48
|
+
font-size: var(--text-base);
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
* Line height is set here rather than inherited from the size token, which
|
|
52
|
+
* carries 20px for 14px text - right for a label, tight for a paragraph.
|
|
53
|
+
* 1.65 is the ratio the eye returns to the start of the next line with; it
|
|
54
|
+
* is a ratio rather than a length so a product that scales the size up for a
|
|
55
|
+
* reading view keeps the proportion.
|
|
56
|
+
*/
|
|
57
|
+
line-height: 1.65;
|
|
58
|
+
color: var(--text);
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* A measure, not a width. Prose is unreadable across a wide window - the eye
|
|
62
|
+
* loses the line it is returning from - and `ch` is the unit that says so in
|
|
63
|
+
* the terms the limit is actually about: characters, at whatever size the
|
|
64
|
+
* text is drawn. 68 is inside the 45-75 the typographic literature agrees
|
|
65
|
+
* on, at the wide end because these are technical texts with code and long
|
|
66
|
+
* identifiers in them.
|
|
67
|
+
*
|
|
68
|
+
* A product that has its own column - a chat bubble, a card - overrides
|
|
69
|
+
* `max-width` and loses nothing else.
|
|
70
|
+
*/
|
|
71
|
+
max-width: 68ch;
|
|
72
|
+
|
|
73
|
+
/*
|
|
74
|
+
* The shell of a desktop app usually turns selection off, so that dragging
|
|
75
|
+
* inside a window moves the window. Text someone came to READ has to hand it
|
|
76
|
+
* back, or the one thing a reader wants to do with a paragraph - take a
|
|
77
|
+
* sentence out of it - is the thing the app forbids.
|
|
78
|
+
*/
|
|
79
|
+
user-select: text;
|
|
80
|
+
-webkit-user-select: text;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/*
|
|
84
|
+
* Vertical rhythm.
|
|
85
|
+
*
|
|
86
|
+
* Margins collapse between siblings, so stating both a top and a bottom on
|
|
87
|
+
* every block would double the gap at some joins and not others depending on
|
|
88
|
+
* which value was larger. Instead: one bottom margin on everything, and the
|
|
89
|
+
* top margin belongs to the headings alone, which are the only elements that
|
|
90
|
+
* need more space above them than below - a heading belongs to what follows
|
|
91
|
+
* it, and sitting equidistant between two paragraphs it appears to belong to
|
|
92
|
+
* neither.
|
|
93
|
+
*/
|
|
94
|
+
.prose > * {
|
|
95
|
+
margin-block: 0 0.75em;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* The first and last child never push the container open: a rendered note
|
|
99
|
+
* inside a bordered card would otherwise have a gap at the top that the card's
|
|
100
|
+
* own padding did not put there. */
|
|
101
|
+
.prose > :first-child {
|
|
102
|
+
margin-block-start: 0;
|
|
103
|
+
}
|
|
104
|
+
.prose > :last-child {
|
|
105
|
+
margin-block-end: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
* Headings.
|
|
110
|
+
*
|
|
111
|
+
* The scale is the theme's, stepped down from a document's h1 - and it starts
|
|
112
|
+
* lower than a web page's would, because prose here is almost always a section
|
|
113
|
+
* INSIDE a screen that already has a title. An h1 drawn at 21px next to a page
|
|
114
|
+
* heading of 18px makes the note look like the more important thing on screen.
|
|
115
|
+
*
|
|
116
|
+
* `text-wrap: balance` on headings only: it is expensive on long text and
|
|
117
|
+
* makes a two-line heading break in the middle rather than leaving one word
|
|
118
|
+
* alone on the second line.
|
|
119
|
+
*/
|
|
120
|
+
.prose :is(h1, h2, h3, h4, h5, h6) {
|
|
121
|
+
margin-block: 1.6em 0.5em;
|
|
122
|
+
font-weight: var(--font-weight-semibold);
|
|
123
|
+
line-height: 1.3;
|
|
124
|
+
text-wrap: balance;
|
|
125
|
+
/* A heading that ends up at the top of a scrolled container should not be
|
|
126
|
+
* flush against its edge. */
|
|
127
|
+
scroll-margin-block-start: 1rem;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.prose h1 {
|
|
131
|
+
font-size: var(--text-xl);
|
|
132
|
+
letter-spacing: var(--tracking-tight);
|
|
133
|
+
}
|
|
134
|
+
.prose h2 {
|
|
135
|
+
font-size: var(--text-lg);
|
|
136
|
+
letter-spacing: var(--tracking-tight);
|
|
137
|
+
}
|
|
138
|
+
.prose h3 {
|
|
139
|
+
font-size: var(--text-base);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/*
|
|
143
|
+
* h4 and below stop growing and start differentiating by other means: a
|
|
144
|
+
* document nested six levels deep has run out of sizes long before it runs out
|
|
145
|
+
* of levels, and inventing two more steps inside four pixels is the noise the
|
|
146
|
+
* type scale exists to remove. They are the body size, set apart by weight and
|
|
147
|
+
* by colour.
|
|
148
|
+
*/
|
|
149
|
+
.prose :is(h4, h5, h6) {
|
|
150
|
+
font-size: var(--text-base);
|
|
151
|
+
color: var(--dim);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* A heading directly after another has nothing between them to separate. */
|
|
155
|
+
.prose :is(h1, h2, h3, h4, h5, h6) + :is(h1, h2, h3, h4, h5, h6) {
|
|
156
|
+
margin-block-start: 0.8em;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/*
|
|
160
|
+
* Lists.
|
|
161
|
+
*
|
|
162
|
+
* Tailwind's preflight strips the markers, which is right for the interface -
|
|
163
|
+
* a menu is a `ul` and must not have bullets - and wrong here, where a list is
|
|
164
|
+
* a list. They are put back, and `outside` so the marker hangs in the indent
|
|
165
|
+
* and the text of a wrapped item lines up with itself rather than with the
|
|
166
|
+
* bullet.
|
|
167
|
+
*/
|
|
168
|
+
.prose :is(ul, ol) {
|
|
169
|
+
padding-inline-start: 1.5em;
|
|
170
|
+
}
|
|
171
|
+
.prose ul {
|
|
172
|
+
list-style: disc;
|
|
173
|
+
}
|
|
174
|
+
.prose ol {
|
|
175
|
+
list-style: decimal;
|
|
176
|
+
}
|
|
177
|
+
/*
|
|
178
|
+
* `--dim`, not `--faint`, and that was measured rather than chosen.
|
|
179
|
+
*
|
|
180
|
+
* A bullet looks like furniture, so the faintest token is the instinct. But
|
|
181
|
+
* the marker is what says "this is a list" - drop it and an ordered list loses
|
|
182
|
+
* its numbers, which are content. On the stand it came out at 3.17:1 against
|
|
183
|
+
* the surface, below the 4.5:1 that anything carrying meaning has to clear,
|
|
184
|
+
* and it read as a smudge at the size a bullet actually is. `--dim` puts it at
|
|
185
|
+
* 6.2:1 and still sits back from the text.
|
|
186
|
+
*/
|
|
187
|
+
.prose li::marker {
|
|
188
|
+
color: var(--dim);
|
|
189
|
+
}
|
|
190
|
+
.prose li {
|
|
191
|
+
margin-block: 0.25em;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* A nested list belongs to the item above it, not to the gap after it. */
|
|
195
|
+
.prose li > :is(ul, ol) {
|
|
196
|
+
margin-block: 0.25em;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* A task list from markdown: the checkbox replaces the marker, so the bullet
|
|
200
|
+
* beside it would be a second one saying the same thing. */
|
|
201
|
+
.prose li:has(> input[type='checkbox']:first-child) {
|
|
202
|
+
list-style: none;
|
|
203
|
+
margin-inline-start: -1.25em;
|
|
204
|
+
}
|
|
205
|
+
.prose li > input[type='checkbox'] {
|
|
206
|
+
margin-inline-end: 0.4em;
|
|
207
|
+
accent-color: var(--accent);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/*
|
|
211
|
+
* Inline code.
|
|
212
|
+
*
|
|
213
|
+
* `0.9em` rather than a token: a monospaced face at the same nominal size as
|
|
214
|
+
* the text around it looks larger, because its lowercase letters are taller
|
|
215
|
+
* relative to the em. The correction is proportional to whatever size the
|
|
216
|
+
* surrounding text happens to be, which a fixed token could not follow.
|
|
217
|
+
*/
|
|
218
|
+
.prose code {
|
|
219
|
+
font-family: var(--font-mono);
|
|
220
|
+
font-size: 0.9em;
|
|
221
|
+
background-color: var(--soft);
|
|
222
|
+
border-radius: var(--radius-xs);
|
|
223
|
+
padding: 0.15em 0.35em;
|
|
224
|
+
/* A long identifier in the middle of a sentence must be allowed to break,
|
|
225
|
+
* or it pushes the whole column wider than its measure. */
|
|
226
|
+
overflow-wrap: anywhere;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/*
|
|
230
|
+
* A code block is not a big inline code. The padding, background and radius
|
|
231
|
+
* belong to the `pre`; the `code` inside it gives them all up, or the block
|
|
232
|
+
* gets a second inset panel drawn inside itself - which is exactly what
|
|
233
|
+
* happens when a typography plugin's inline rule is left to apply here.
|
|
234
|
+
*
|
|
235
|
+
* `overflow-x: auto` rather than wrapping: a wrapped line of code is a line
|
|
236
|
+
* that lies about where it ends, and indentation is how code is read.
|
|
237
|
+
*/
|
|
238
|
+
.prose pre {
|
|
239
|
+
font-family: var(--font-mono);
|
|
240
|
+
font-size: var(--text-sm);
|
|
241
|
+
line-height: 1.55;
|
|
242
|
+
background-color: var(--soft);
|
|
243
|
+
border: 1px solid var(--line);
|
|
244
|
+
border-radius: var(--radius-md);
|
|
245
|
+
padding: 0.75rem 0.85rem;
|
|
246
|
+
overflow-x: auto;
|
|
247
|
+
/* `tab-size: 2` is the line's own indent; the browser default of 8 turns a
|
|
248
|
+
* tab-indented file into a horizontal scroll for nothing. */
|
|
249
|
+
tab-size: 2;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.prose pre code {
|
|
253
|
+
background-color: transparent;
|
|
254
|
+
border-radius: 0;
|
|
255
|
+
padding: 0;
|
|
256
|
+
font-size: inherit;
|
|
257
|
+
/* Inside a scrolling block, breaking a long line would defeat the scroll. */
|
|
258
|
+
overflow-wrap: normal;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/*
|
|
262
|
+
* Links.
|
|
263
|
+
*
|
|
264
|
+
* `--accent-2` rather than `--accent`: on the dark theme the accent is the
|
|
265
|
+
* product's own colour at full strength, which against body text reads as a
|
|
266
|
+
* button that failed to draw. The partner shade is the one the products use
|
|
267
|
+
* for a link, and it is also the one with room to darken on hover.
|
|
268
|
+
*
|
|
269
|
+
* Underlined, always. Colour alone is not a link - a reader who does not see
|
|
270
|
+
* the hue gets no signal at all - and this is the one place in the system
|
|
271
|
+
* where the rule "meaning never rests on colour" has a standard answer.
|
|
272
|
+
*/
|
|
273
|
+
.prose a {
|
|
274
|
+
color: var(--accent-2);
|
|
275
|
+
text-decoration: underline;
|
|
276
|
+
/* The underline drops below the descenders instead of striking through
|
|
277
|
+
* them, which is the difference between a link and a crossed-out word. */
|
|
278
|
+
text-underline-offset: 0.2em;
|
|
279
|
+
text-decoration-thickness: from-font;
|
|
280
|
+
overflow-wrap: anywhere;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.prose a:hover {
|
|
284
|
+
color: var(--accent);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/*
|
|
288
|
+
* A quotation.
|
|
289
|
+
*
|
|
290
|
+
* A rule on the left and dimmed text, rather than italics: a blockquote is
|
|
291
|
+
* frequently a paragraph or more, and a long passage in italic is slower to
|
|
292
|
+
* read for everyone and materially harder for some dyslexic readers.
|
|
293
|
+
*/
|
|
294
|
+
.prose blockquote {
|
|
295
|
+
border-inline-start: 2px solid var(--line-2);
|
|
296
|
+
padding-inline-start: 0.9em;
|
|
297
|
+
color: var(--dim);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/*
|
|
301
|
+
* A table inside prose.
|
|
302
|
+
*
|
|
303
|
+
* `display: block` with its own scroll, because the one thing a table must not
|
|
304
|
+
* do in a reading column is set the column's width: a note with a six-column
|
|
305
|
+
* table in it would push every paragraph around it out to the table's width.
|
|
306
|
+
* The cost is stated - a block-level table no longer participates in the
|
|
307
|
+
* column's own layout - and it is the right trade for text.
|
|
308
|
+
*/
|
|
309
|
+
.prose table {
|
|
310
|
+
display: block;
|
|
311
|
+
max-width: 100%;
|
|
312
|
+
overflow-x: auto;
|
|
313
|
+
border-collapse: collapse;
|
|
314
|
+
font-size: var(--text-sm);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.prose :is(th, td) {
|
|
318
|
+
border: 1px solid var(--line);
|
|
319
|
+
padding: 0.3em 0.55em;
|
|
320
|
+
text-align: start;
|
|
321
|
+
vertical-align: top;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.prose th {
|
|
325
|
+
background-color: var(--soft);
|
|
326
|
+
font-weight: var(--font-weight-semibold);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/*
|
|
330
|
+
* A horizontal rule is a section break, so the space around it is the point;
|
|
331
|
+
* a hairline with a paragraph's gap either side reads as a mistake.
|
|
332
|
+
*/
|
|
333
|
+
.prose hr {
|
|
334
|
+
border: 0;
|
|
335
|
+
border-block-start: 1px solid var(--line);
|
|
336
|
+
margin-block: 2em;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.prose :is(strong, b) {
|
|
340
|
+
font-weight: var(--font-weight-semibold);
|
|
341
|
+
color: var(--text);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.prose :is(em, i) {
|
|
345
|
+
font-style: italic;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.prose :is(s, del) {
|
|
349
|
+
color: var(--dim);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.prose mark {
|
|
353
|
+
background-color: var(--accent-soft);
|
|
354
|
+
color: inherit;
|
|
355
|
+
border-radius: var(--radius-xs);
|
|
356
|
+
padding: 0.05em 0.2em;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/*
|
|
360
|
+
* An image in prose is never wider than the column, and keeps its ratio when
|
|
361
|
+
* it is constrained. `display: block` because an inline image sits on the text
|
|
362
|
+
* baseline and leaves a strip of descender space under it that looks like a
|
|
363
|
+
* broken margin.
|
|
364
|
+
*/
|
|
365
|
+
.prose img {
|
|
366
|
+
display: block;
|
|
367
|
+
max-width: 100%;
|
|
368
|
+
height: auto;
|
|
369
|
+
border-radius: var(--radius-sm);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/*
|
|
373
|
+
* `kbd` is drawn as a key rather than as code: a reader who sees `Ctrl` in the
|
|
374
|
+
* same grey box as a variable name has to work out which it is. This matches
|
|
375
|
+
* the Kbd primitive, so a key looks the same whether a component drew it or
|
|
376
|
+
* markdown did.
|
|
377
|
+
*/
|
|
378
|
+
.prose kbd {
|
|
379
|
+
font-family: var(--font-mono);
|
|
380
|
+
font-size: 0.85em;
|
|
381
|
+
background-color: var(--raise);
|
|
382
|
+
border: 1px solid var(--line-2);
|
|
383
|
+
border-block-end-width: 2px;
|
|
384
|
+
border-radius: var(--radius-xs);
|
|
385
|
+
padding: 0.1em 0.35em;
|
|
386
|
+
color: var(--dim);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/*
|
|
390
|
+
* A definition list, which markdown does not produce but a CMS does.
|
|
391
|
+
*/
|
|
392
|
+
.prose dt {
|
|
393
|
+
font-weight: var(--font-weight-semibold);
|
|
394
|
+
margin-block-start: 0.75em;
|
|
395
|
+
}
|
|
396
|
+
.prose dd {
|
|
397
|
+
margin-inline-start: 1.5em;
|
|
398
|
+
color: var(--dim);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/*
|
|
402
|
+
* A footnote reference and the notes at the bottom - what `remark-gfm`
|
|
403
|
+
* produces. Smaller and dimmer, because a footnote that reads at the weight of
|
|
404
|
+
* the text interrupts the sentence carrying it.
|
|
405
|
+
*/
|
|
406
|
+
.prose sup a {
|
|
407
|
+
text-decoration: none;
|
|
408
|
+
font-size: 0.8em;
|
|
409
|
+
}
|
|
410
|
+
.prose .footnotes {
|
|
411
|
+
font-size: var(--text-sm);
|
|
412
|
+
color: var(--dim);
|
|
413
|
+
border-block-start: 1px solid var(--line);
|
|
414
|
+
margin-block-start: 2em;
|
|
415
|
+
padding-block-start: 0.75em;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/*
|
|
419
|
+
* Tight: the same prose in a place that has no room for a reading column - a
|
|
420
|
+
* chat bubble, a table cell, a hover card. The rhythm compresses and the
|
|
421
|
+
* measure is given up to the container, because in a bubble the container IS
|
|
422
|
+
* the measure. Nothing else changes: the same tags, the same tokens.
|
|
423
|
+
*/
|
|
424
|
+
.prose-tight {
|
|
425
|
+
font-size: var(--text-sm);
|
|
426
|
+
line-height: 1.55;
|
|
427
|
+
max-width: none;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.prose-tight > * {
|
|
431
|
+
margin-block: 0 0.5em;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.prose-tight :is(h1, h2, h3, h4, h5, h6) {
|
|
435
|
+
margin-block: 1em 0.35em;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.prose-tight :is(h1, h2) {
|
|
439
|
+
font-size: var(--text-base);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.prose-tight h3 {
|
|
443
|
+
font-size: var(--text-sm);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.prose-tight hr {
|
|
447
|
+
margin-block: 1.2em;
|
|
448
|
+
}
|