@threadlabs/looma-docs 0.0.0-stage → 0.52.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/LICENSE +21 -0
- package/README.md +11 -2
- package/bin/looma-docs.mjs +3 -0
- package/docs/README.md +89 -0
- package/docs/card.html +79 -0
- package/docs/card.js +18 -0
- package/docs/catalog-view.js +24 -0
- package/docs/catalog-view.mjs +13 -0
- package/docs/catalog.mjs +135 -0
- package/docs/cli.mjs +26 -0
- package/docs/commands.mjs +94 -0
- package/docs/customizer-render.mjs +157 -0
- package/docs/customizer.js +494 -0
- package/docs/documentation.css +42 -0
- package/docs/example-invocations.mjs +63 -0
- package/docs/fonts.mjs +73 -0
- package/docs/framework-examples.d.mts +9 -0
- package/docs/framework-examples.mjs +225 -0
- package/docs/guides/01.theme-your-system.md +64 -0
- package/docs/guides/02.add-your-components.md +96 -0
- package/docs/guides/03.publish-your-system.md +71 -0
- package/docs/guides/04.theme-decisions.md +141 -0
- package/docs/guides/05.(Theme reference)/01.theme-colors.md +95 -0
- package/docs/guides/05.(Theme reference)/02.theme-typography.md +73 -0
- package/docs/guides/05.(Theme reference)/03.theme-spacing.md +103 -0
- package/docs/guides/05.(Theme reference)/04.theme-material.md +90 -0
- package/docs/guides/05.(Theme reference)/05.theme-motion.md +57 -0
- package/docs/guides/assets/fellowship-dark.png +0 -0
- package/docs/guides/assets/fellowship-light.png +0 -0
- package/docs/index.d.ts +59 -0
- package/docs/index.mjs +4 -0
- package/docs/local-server.mjs +65 -0
- package/docs/navigation.mjs +40 -0
- package/docs/palette.mjs +68 -0
- package/docs/pastoral.svg +54 -0
- package/docs/portrait.svg +15 -0
- package/docs/reference.html +1 -0
- package/docs/reference.js +36 -0
- package/docs/render.mjs +97 -0
- package/docs/site.mjs +244 -0
- package/docs/store.mjs +92 -0
- package/docs/theme-session.mjs +72 -0
- package/docs/theme.mjs +669 -0
- package/docs/value-editors.mjs +37 -0
- package/docs/workspace.mjs +32 -0
- package/package.json +53 -3
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Theme decisions
|
|
3
|
+
order: 4
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<div data-looma-guide>
|
|
7
|
+
|
|
8
|
+
# Theme decisions
|
|
9
|
+
|
|
10
|
+
A theme starts with decisions such as “forest green actions,” “serif headings,” and “roomier controls.” Looma turns those decisions into shared CSS properties that your components use.
|
|
11
|
+
|
|
12
|
+
You can make those decisions in **Customize**, or edit the JSON recipe and regenerate the CSS. This guide follows one decision through that process, then maps each family in detail.
|
|
13
|
+
|
|
14
|
+
## Follow a decision from recipe to component
|
|
15
|
+
|
|
16
|
+
<link rel="component" href="@threadlabs/looma/components/ui-grid/ui-grid.html">
|
|
17
|
+
<link rel="component" href="@threadlabs/looma/components/ui-card/ui-card.html">
|
|
18
|
+
<link rel="component" href="@threadlabs/looma/components/ui-stack/ui-stack.html">
|
|
19
|
+
<link rel="component" href="@threadlabs/looma/components/ui-text/ui-text.html">
|
|
20
|
+
|
|
21
|
+
<ui-card padding="md">
|
|
22
|
+
<ui-grid columns="3" min="sm" gap="l">
|
|
23
|
+
<ui-stack gap="s">
|
|
24
|
+
<ui-text variant="eyebrow" tone="muted">01 · Decide</ui-text>
|
|
25
|
+
<ui-text variant="heading" as="h3">A roomier control</ui-text>
|
|
26
|
+
<ui-text variant="code" size="sm" wrap="anywhere">control-roominess: 2.75</ui-text>
|
|
27
|
+
<ui-text variant="caption" tone="secondary">Keep the choice in your JSON recipe.</ui-text>
|
|
28
|
+
</ui-stack>
|
|
29
|
+
<ui-stack gap="s">
|
|
30
|
+
<ui-text variant="eyebrow" tone="muted">02 · Derive</ui-text>
|
|
31
|
+
<ui-text variant="heading" as="h3">One size family</ui-text>
|
|
32
|
+
<ui-text variant="code" size="sm">16px × 2.75 = 44px</ui-text>
|
|
33
|
+
<ui-text variant="caption" tone="secondary">Generate CSS inputs and the rules that derive related sizes.</ui-text>
|
|
34
|
+
</ui-stack>
|
|
35
|
+
<ui-stack gap="s">
|
|
36
|
+
<ui-text variant="eyebrow" tone="muted">03 · Apply</ui-text>
|
|
37
|
+
<ui-text variant="heading" as="h3">Components agree</ui-text>
|
|
38
|
+
<ui-text variant="code" size="sm">size="md"</ui-text>
|
|
39
|
+
<ui-text variant="caption" tone="secondary">A medium button and medium input use the same height family.</ui-text>
|
|
40
|
+
</ui-stack>
|
|
41
|
+
</ui-grid>
|
|
42
|
+
</ui-card>
|
|
43
|
+
|
|
44
|
+
::: table
|
|
45
|
+
|
|
46
|
+
- Step: 1. Choose the intent
|
|
47
|
+
Example: “Make controls a little roomier.”
|
|
48
|
+
Where it lives: **Size & spacing → Control roominess** in Customize.
|
|
49
|
+
|
|
50
|
+
- Step: 2. Keep the decision
|
|
51
|
+
Example: `"control-roominess": "2.75"`
|
|
52
|
+
Where it lives: `values` in `looma.theme.json`.
|
|
53
|
+
|
|
54
|
+
- Step: 3. Generate the CSS
|
|
55
|
+
Example: `--ui-control-roominess: 2.75;`
|
|
56
|
+
Where it lives: `looma.theme.css`, alongside the shared derivation rules.
|
|
57
|
+
|
|
58
|
+
- Step: 4. Derive the family
|
|
59
|
+
Example: A 16px text base gives a 44px medium control; the size ratio derives small and large.
|
|
60
|
+
Where it lives: Shared CSS roles such as `--ui-control-size-md`.
|
|
61
|
+
|
|
62
|
+
- Step: 5. Use the result
|
|
63
|
+
Example: A medium button and medium input take their height from the same family.
|
|
64
|
+
Where it lives: Looma's component definitions, and your components when they use those public roles.
|
|
65
|
+
|
|
66
|
+
:::
|
|
67
|
+
|
|
68
|
+
The JSON keeps **what you chose**. The CSS carries **what the application renders**. Commit both, so you can reopen the customizer and reproduce the result.
|
|
69
|
+
|
|
70
|
+
## Read the two files together
|
|
71
|
+
|
|
72
|
+
This is a small recipe with explicit choices. Omitted values use the generator's defaults; a starting theme supplies a fuller set.
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"schemaVersion": 1,
|
|
77
|
+
"values": {
|
|
78
|
+
"text-size": "16px",
|
|
79
|
+
"control-roominess": "2.75",
|
|
80
|
+
"scale-contrast": "1.2",
|
|
81
|
+
"display-font": "Georgia, serif",
|
|
82
|
+
"response-time": "150ms"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The generated stylesheet includes these authoring properties:
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
:root {
|
|
91
|
+
--ui-font-size: 16px;
|
|
92
|
+
--ui-control-roominess: 2.75;
|
|
93
|
+
--ui-scale-ratio: 1.2;
|
|
94
|
+
--ui-font-display: Georgia, serif;
|
|
95
|
+
--ui-motion-fast: 150ms;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
That excerpt is only the input layer. The saved CSS also includes light and dark colors and the derivation rules for sizes, type, spacing, and material. Use the complete exported file in your application.
|
|
100
|
+
|
|
101
|
+
## Choose the family you want to understand
|
|
102
|
+
|
|
103
|
+
- [Color](/theme-colors/): brand seeds, light/dark palettes, semantic statuses, and readable foregrounds.
|
|
104
|
+
- [Typography](/theme-typography/): body and heading fonts, base size, labels, and emphasis.
|
|
105
|
+
- [Size & spacing](/theme-spacing/): control height, the size ladder, rhythm, gaps, corners, and borders.
|
|
106
|
+
- [Material & light](/theme-material/): depth, reflection, transparency, and the light source.
|
|
107
|
+
- [Motion](/theme-motion/): feedback duration, easing, and reduced motion.
|
|
108
|
+
|
|
109
|
+
Each page connects recipe keys to CSS properties and the result you can see. The [token guide](https://threadlabs.studio/looma/tokens/) explains how to use public properties in your own component styles. The [theme reference](https://threadlabs.studio/looma/theme-reference/) lists the complete property defaults and ranges.
|
|
110
|
+
|
|
111
|
+
## Color has an extra generation step
|
|
112
|
+
|
|
113
|
+
A geometry decision such as `control-roominess` becomes a CSS property that the shared rules can use directly. A color decision can affect many resolved properties at once.
|
|
114
|
+
|
|
115
|
+
For example, an `action` seed contributes to the accent family, readable text on a solid action, contextual action text, and interaction states. `warmth` changes how the palette is generated; it has no matching `--ui-warmth` property. Edit that decision in the recipe or customizer, then regenerate.
|
|
116
|
+
|
|
117
|
+
See [Color](/theme-colors/) for the full path from seeds to usable roles.
|
|
118
|
+
|
|
119
|
+
## Use the theme in your own components
|
|
120
|
+
|
|
121
|
+
Choose semantic roles for what the element means. A reading surface can use `--ui-surface-raised` and `--ui-text-primary`; supporting copy can use `--ui-text-secondary`. The generated palette supplies the right values for light and dark.
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
.reading-note {
|
|
125
|
+
background: var(--ui-surface-raised);
|
|
126
|
+
color: var(--ui-text-primary);
|
|
127
|
+
border: var(--ui-border-width) solid var(--ui-border);
|
|
128
|
+
border-radius: var(--ui-radius-md);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Put that CSS in your own HTML Next definition. Compose Looma controls through their props and supported public properties, so their shared layout and behavior stay consistent.
|
|
133
|
+
|
|
134
|
+
## Keep the right source editable
|
|
135
|
+
|
|
136
|
+
- Change the recipe in Customize, save it, and load the generated CSS in your application.
|
|
137
|
+
- Put handwritten adjustments in a separate overrides file.
|
|
138
|
+
- Re-save the recipe after a Looma upgrade to adopt updated generation rules.
|
|
139
|
+
- Use `data-theme="light"` or `data-theme="dark"` for a fixed mode, or leave it off the root to follow the system.
|
|
140
|
+
|
|
141
|
+
</div>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Color
|
|
3
|
+
order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<div data-looma-guide>
|
|
7
|
+
|
|
8
|
+
# Color
|
|
9
|
+
|
|
10
|
+
Choose the colors your product starts from. Looma derives the related roles that buttons, fields, text, surfaces, and statuses need in light and dark.
|
|
11
|
+
|
|
12
|
+
## From a brand seed to usable colors
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
Action seed + canvas + surface + text
|
|
16
|
+
↓
|
|
17
|
+
Palette relationships and readability checks
|
|
18
|
+
↓
|
|
19
|
+
Light and dark semantic roles
|
|
20
|
+
↓
|
|
21
|
+
Action fills, labels, links, focus, selection, and statuses
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`action` is a starting pigment. The generator chooses readable on-fill text separately and resolves contextual text and interaction states against their surfaces. A seed may be adjusted when needed for readability.
|
|
25
|
+
|
|
26
|
+
## Start with four seeds
|
|
27
|
+
|
|
28
|
+
::: table
|
|
29
|
+
|
|
30
|
+
- Recipe key: `action`
|
|
31
|
+
Public CSS role: `--ui-accent`
|
|
32
|
+
What you see: The primary action family, with readable labels and states derived separately.
|
|
33
|
+
|
|
34
|
+
- Recipe key: `canvas`
|
|
35
|
+
Public CSS role: `--ui-surface`
|
|
36
|
+
What you see: The page's ground behind its content surfaces.
|
|
37
|
+
|
|
38
|
+
- Recipe key: `surface`
|
|
39
|
+
Public CSS role: `--ui-surface-raised`
|
|
40
|
+
What you see: Cards, fields, and floating content surfaces.
|
|
41
|
+
|
|
42
|
+
- Recipe key: `text`
|
|
43
|
+
Public CSS role: `--ui-text`
|
|
44
|
+
What you see: The starting text color; usable primary and supporting text roles are derived against surfaces.
|
|
45
|
+
|
|
46
|
+
:::
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"schemaVersion": 1,
|
|
51
|
+
"values": {
|
|
52
|
+
"action": "#315c48",
|
|
53
|
+
"canvas": "#faf8f1",
|
|
54
|
+
"surface": "#ffffff",
|
|
55
|
+
"text": "#29382e"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
These are the seed choices used in [Fellowship UI](https://github.com/threadlabs-studio/looma/tree/main/examples/fellowship-ui). The generated colors can differ from the seeds to keep the finished roles readable.
|
|
61
|
+
|
|
62
|
+
## Refine relationships
|
|
63
|
+
|
|
64
|
+
These keys guide palette generation. They live in the recipe; there is no CSS property with the same purpose to turn later.
|
|
65
|
+
|
|
66
|
+
::: table
|
|
67
|
+
|
|
68
|
+
- Recipe keys: `warmth`, `color-intensity`, `palette-relationship`
|
|
69
|
+
Decision: Cool or warm paper and ink, quiet or vivid color, and how quiet surfaces relate to the brand.
|
|
70
|
+
|
|
71
|
+
- Recipe keys: `secondary-action`, `tertiary-action`
|
|
72
|
+
Decision: Optional supporting brand families. Components expose them as `tone="accent-secondary"` and `tone="accent-tertiary"`; omitted accents follow primary.
|
|
73
|
+
|
|
74
|
+
- Recipe keys: `neutral-source`, `neutral-strength`, `neutral-border-contrast`
|
|
75
|
+
Decision: Where neutral pigment comes from, how strongly it fills controls, and the minimum boundary contrast shared by neutral actions and fields.
|
|
76
|
+
|
|
77
|
+
- Recipe keys: `status-chroma`, `success-source`, `danger-source`, `warning-source`, `info-source`
|
|
78
|
+
Decision: Status saturation and whether each status keeps a semantic hue, harmonizes toward primary, or uses a brand family.
|
|
79
|
+
|
|
80
|
+
- Recipe keys: `selection-accent`, `focus-accent`, `link-accent`
|
|
81
|
+
Decision: Which brand family speaks through selected rows, keyboard focus, and inline links.
|
|
82
|
+
|
|
83
|
+
:::
|
|
84
|
+
|
|
85
|
+
Warmth and color intensity use 0.5 as their original-seed midpoint. Palette relationship preserves seed hues, relates quiet surfaces to neighboring hues, or uses complementary hues. Neutral fill strength and boundary contrast remain separate decisions. Status relationships change the palette, while words and icons continue to carry status meaning.
|
|
86
|
+
|
|
87
|
+
## Use semantic roles in components
|
|
88
|
+
|
|
89
|
+
Choose the role for the job: `--ui-text-primary` for readable content, `--ui-text-secondary` for supporting text, and `--ui-control-border` for the shared neutral control boundary. A semantic role carries the theme's light/dark result and the intended relationship to nearby surfaces.
|
|
90
|
+
|
|
91
|
+
Every exported theme contains both modes. Set `data-theme="dark"` on a region to give that subtree a dark palette, including inside a light page. A floating surface needs to retain the mode of the region it came from.
|
|
92
|
+
|
|
93
|
+
[Back to Theme decisions](/theme-decisions/) · [Next: typography](/theme-typography/)
|
|
94
|
+
|
|
95
|
+
</div>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Typography
|
|
3
|
+
order: 2
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<div data-looma-guide>
|
|
7
|
+
|
|
8
|
+
# Typography
|
|
9
|
+
|
|
10
|
+
Set the shared reading voice, then decide whether headings need their own face. Type roles keep headings, labels, captions, and body text related across components.
|
|
11
|
+
|
|
12
|
+
## Map your decisions
|
|
13
|
+
|
|
14
|
+
::: table
|
|
15
|
+
|
|
16
|
+
- Recipe key: `body-font`
|
|
17
|
+
CSS property: `--ui-font-sans`
|
|
18
|
+
What changes: Body text and the shared interface typeface used by controls.
|
|
19
|
+
|
|
20
|
+
- Recipe key: `display-font`
|
|
21
|
+
CSS property: `--ui-font-display`
|
|
22
|
+
What changes: Heading and display text.
|
|
23
|
+
|
|
24
|
+
- Recipe key: `text-size`
|
|
25
|
+
CSS property: `--ui-font-size`
|
|
26
|
+
What changes: The document's px text base. Type, geometry, and spacing derive their rem relationships from it once.
|
|
27
|
+
|
|
28
|
+
- Recipe key: `label-size`
|
|
29
|
+
CSS property: `--ui-field-label-font-size`
|
|
30
|
+
What changes: Field labels. `inherit` follows the body size; a separate length makes labels independent. Help text stays supporting text.
|
|
31
|
+
|
|
32
|
+
- Recipe key: `text-weight`
|
|
33
|
+
CSS property: `--ui-font-medium`
|
|
34
|
+
What changes: Shared emphasis such as control labels, while body and headings retain their roles.
|
|
35
|
+
|
|
36
|
+
:::
|
|
37
|
+
|
|
38
|
+
## Try a body and heading pair
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"schemaVersion": 1,
|
|
43
|
+
"values": {
|
|
44
|
+
"body-font": "system-ui, sans-serif",
|
|
45
|
+
"display-font": "Georgia, serif",
|
|
46
|
+
"text-size": "16px",
|
|
47
|
+
"label-size": "inherit"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This pair uses an operating-system interface face and a serif heading stack. A font stack selects fonts already available to the application. Load custom or self-hosted faces in the application itself.
|
|
53
|
+
|
|
54
|
+
In Customize, a Google Fonts selection previews the real face and includes its import in the saved CSS. The recipe's optional `fonts` mapping keeps the selected Google family names; `values` keeps the matching CSS stacks. Export and import preserve both.
|
|
55
|
+
|
|
56
|
+
## Separate meaning from appearance
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<ui-text variant="display" as="h1">The field guide</ui-text>
|
|
60
|
+
<ui-text variant="heading" as="h2">Before you begin</ui-text>
|
|
61
|
+
<ui-text variant="body">A practical introduction for your team.</ui-text>
|
|
62
|
+
<ui-text variant="caption" tone="muted">Updated this week</ui-text>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`variant` chooses a visual role. `as` chooses the document element. You can use a heading's appearance at the appropriate heading level without changing its place in the document outline.
|
|
66
|
+
|
|
67
|
+
## Check the result
|
|
68
|
+
|
|
69
|
+
Compare a full heading, a paragraph, a field label, and supporting text together. Then check a medium button beside a medium field. A larger text base also changes the size relationships explained in [Size & spacing](/theme-spacing/).
|
|
70
|
+
|
|
71
|
+
[Back to Theme decisions](/theme-decisions/) · [Next: size and spacing](/theme-spacing/)
|
|
72
|
+
|
|
73
|
+
</div>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Size & spacing
|
|
3
|
+
order: 3
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<div data-looma-guide>
|
|
7
|
+
|
|
8
|
+
# Size & spacing
|
|
9
|
+
|
|
10
|
+
Control height, surrounding space, and vertical rhythm are related decisions with separate jobs. Set the shared family once, then use component size and layout props to compose it.
|
|
11
|
+
|
|
12
|
+
## Follow a control height
|
|
13
|
+
|
|
14
|
+
With an explicit 16px text base and a size ratio of 1.2:
|
|
15
|
+
|
|
16
|
+
::: table
|
|
17
|
+
|
|
18
|
+
- Decision: Control roominess `2.5`
|
|
19
|
+
Small: About 33.3px
|
|
20
|
+
Medium: 40px
|
|
21
|
+
Large: 48px
|
|
22
|
+
|
|
23
|
+
- Decision: Control roominess `2.75`
|
|
24
|
+
Small: About 36.7px
|
|
25
|
+
Medium: 44px
|
|
26
|
+
Large: 52.8px
|
|
27
|
+
|
|
28
|
+
:::
|
|
29
|
+
|
|
30
|
+
The relationship is `medium = base text size × roominess`, `small = medium ÷ size ratio`, and `large = medium × size ratio`. These values describe the shared height roles. Content, borders, and component-specific constraints can still require more room.
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
text-size: 16px control-roominess: 2.75
|
|
34
|
+
↘ ↙
|
|
35
|
+
medium: 44px
|
|
36
|
+
↙ ↘
|
|
37
|
+
small: 44 ÷ 1.2 large: 44 × 1.2
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A 44px example is the result of these chosen values. Choose the proportions appropriate to your interface. Touch-target area is a separate decision and can extend beyond a compact visible control where neighboring controls allow it.
|
|
41
|
+
|
|
42
|
+
## Map your decisions
|
|
43
|
+
|
|
44
|
+
::: table
|
|
45
|
+
|
|
46
|
+
- Recipe key: `control-roominess`
|
|
47
|
+
CSS property: `--ui-control-roominess`
|
|
48
|
+
What changes: Medium control height as a unitless multiple of the text base. Derives `--ui-control-size` and its small, medium, and large roles.
|
|
49
|
+
|
|
50
|
+
- Recipe key: `scale-contrast`
|
|
51
|
+
CSS property: `--ui-scale-ratio`
|
|
52
|
+
What changes: The separation between sizes. At 1, the control sizes are equal; higher ratios separate them.
|
|
53
|
+
|
|
54
|
+
- Recipe key: `rhythm`
|
|
55
|
+
CSS property: `--ui-rhythm`
|
|
56
|
+
What changes: Vertical Stack gaps and unitless line-height multipliers. Horizontal navigation and action-row gaps retain their own spacing.
|
|
57
|
+
|
|
58
|
+
- Recipe key: `spacing`
|
|
59
|
+
CSS property: `--ui-spacing`
|
|
60
|
+
What changes: Gaps and padding independently of control heights and text ratios.
|
|
61
|
+
|
|
62
|
+
- Recipe key: `space-curve`
|
|
63
|
+
CSS property: `--ui-space-curve`
|
|
64
|
+
What changes: How the spacing scale grows. 1 is linear; increasing toward 2 curves the intermediate steps while keeping stable endpoints.
|
|
65
|
+
|
|
66
|
+
- Recipe key: `corner-roundness`
|
|
67
|
+
CSS property: `--ui-radius-md`
|
|
68
|
+
What changes: The shared corner family for controls and surfaces.
|
|
69
|
+
|
|
70
|
+
- Recipe key: `line-weight`
|
|
71
|
+
CSS property: `--ui-border-width`
|
|
72
|
+
What changes: Shared border weight. Focus visibility has its own roles.
|
|
73
|
+
|
|
74
|
+
:::
|
|
75
|
+
|
|
76
|
+
## Keep height and space distinct
|
|
77
|
+
|
|
78
|
+
If buttons and fields feel too tall, change **Control roominess**. If content feels crowded around otherwise well-proportioned controls, change **Spacing**. If stacked paragraphs and groups need more vertical breathing room, change **Rhythm**.
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"schemaVersion": 1,
|
|
83
|
+
"values": {
|
|
84
|
+
"text-size": "16px",
|
|
85
|
+
"control-roominess": "2.75",
|
|
86
|
+
"scale-contrast": "1.2",
|
|
87
|
+
"spacing": "1.1",
|
|
88
|
+
"rhythm": "1.1"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
In component markup, use supported props such as `size="sm"` on a control or `gap="l"` on Stack. Let the theme supply the shared values behind those choices.
|
|
94
|
+
|
|
95
|
+
## Preserve units
|
|
96
|
+
|
|
97
|
+
Base `text-size` is px-only and sets the document root. Other supported length inputs retain their CSS units, including rem, with generated guards for their documented bounds. Set the base on `:root`; a nested light/dark region shares that rem root.
|
|
98
|
+
|
|
99
|
+
For older recipes, replace `density` with `spacing`. Replace a length-valued `control-size` with numeric `control-roominess` by dividing the desired medium height by the original text base. For example, `40px ÷ 16px` becomes `2.5`.
|
|
100
|
+
|
|
101
|
+
[Back to Theme decisions](/theme-decisions/) · [Next: material and light](/theme-material/)
|
|
102
|
+
|
|
103
|
+
</div>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Material & light
|
|
3
|
+
order: 4
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<div data-looma-guide>
|
|
7
|
+
|
|
8
|
+
# Material & light
|
|
9
|
+
|
|
10
|
+
Choose whether surfaces feel flat, raised, matte, polished, or glassy. A shared light source gives their reflection and depth a consistent direction.
|
|
11
|
+
|
|
12
|
+
## Give each decision a job
|
|
13
|
+
|
|
14
|
+
::: table
|
|
15
|
+
|
|
16
|
+
- Recipe key: `outer-depth`
|
|
17
|
+
CSS property: `--ui-outer-depth`
|
|
18
|
+
What changes: How strongly controls and floating surfaces rise above the page.
|
|
19
|
+
|
|
20
|
+
- Recipe key: `inner-depth`
|
|
21
|
+
CSS property: `--ui-inner-depth`
|
|
22
|
+
What changes: Recession in fields and pressed controls.
|
|
23
|
+
|
|
24
|
+
- Recipe key: `finish`
|
|
25
|
+
CSS property: `--ui-finish`
|
|
26
|
+
What changes: How strongly a surface catches a reflection, from matte to polished. Reflection also needs nonzero light strength.
|
|
27
|
+
|
|
28
|
+
- Recipe key: `glassiness`
|
|
29
|
+
CSS property: `--ui-glassiness`
|
|
30
|
+
What changes: Frosting of the scene behind glass, primarily through decorative rims.
|
|
31
|
+
|
|
32
|
+
- Recipe key: `surface-separation`
|
|
33
|
+
CSS property: `--ui-surface-separation`
|
|
34
|
+
What changes: The material offset around floating content, scaled by the popup's purpose and size.
|
|
35
|
+
|
|
36
|
+
- Recipe key: `translucency`
|
|
37
|
+
CSS property: `--ui-overlay-translucency`
|
|
38
|
+
What changes: Transmission through decorative rims. At maximum, content plates also reveal a little of the scene.
|
|
39
|
+
|
|
40
|
+
- Recipe key: `edge-light`
|
|
41
|
+
CSS property: `--ui-edge-light`
|
|
42
|
+
What changes: Light caught at the boundary, independently of depth.
|
|
43
|
+
|
|
44
|
+
- Recipe key: `glow`
|
|
45
|
+
CSS property: `--ui-glow`
|
|
46
|
+
What changes: Reflected light beyond the surface, from a quiet halo to a stronger bloom.
|
|
47
|
+
|
|
48
|
+
:::
|
|
49
|
+
|
|
50
|
+
Depth changes elevation. Finish changes reflection. Transparency changes what shows through. You can change one while retaining the others: turning light strength to zero removes illumination while depth shadows remain.
|
|
51
|
+
|
|
52
|
+
## Place the light
|
|
53
|
+
|
|
54
|
+
::: table
|
|
55
|
+
|
|
56
|
+
- Recipe key: `light-color`
|
|
57
|
+
CSS property: `--ui-light-color`
|
|
58
|
+
What changes: The reflected environment's color, independently of object pigment.
|
|
59
|
+
|
|
60
|
+
- Recipe keys: `light-x`, `light-y`
|
|
61
|
+
CSS properties: `--ui-light-x`, `--ui-light-y`
|
|
62
|
+
What changes: Position relative to each surface. Coordinates run from 0 to 1; `0.5, 0` is top center.
|
|
63
|
+
|
|
64
|
+
- Recipe key: `light-spread`
|
|
65
|
+
CSS property: `--ui-light-spread`
|
|
66
|
+
What changes: Concentrated illumination through a broader light source.
|
|
67
|
+
|
|
68
|
+
- Recipe key: `light-strength`
|
|
69
|
+
CSS property: `--ui-light-strength`
|
|
70
|
+
What changes: Illumination intensity. Components and materials determine how they respond.
|
|
71
|
+
|
|
72
|
+
:::
|
|
73
|
+
|
|
74
|
+
In **Customize → Material & light**, drag the light source, use its arrow keys, or enter exact percentages. Shift moves faster; Home returns it to top center. Escape cancels a drag and restores its start.
|
|
75
|
+
|
|
76
|
+
## Let components choose the treatment
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<ui-card variant="elevated">A standard raised card</ui-card>
|
|
80
|
+
<ui-card variant="elevated" treatment="expressive">A raised card with the theme's material</ui-card>
|
|
81
|
+
<ui-card treatment="expressive">Material treatment on an outlined card</ui-card>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`variant` chooses surface structure. `treatment` chooses how much decorative material it expresses. A matte theme stays matte even when a component requests expressive treatment. A card's tone changes its color while preserving its geometry.
|
|
85
|
+
|
|
86
|
+
The content plane stays nearly opaque so text remains readable. Reduced-transparency and forced-colors preferences suppress ornamental reflection and glow; reduced transparency also disables backdrop blur.
|
|
87
|
+
|
|
88
|
+
[Back to Theme decisions](/theme-decisions/) · [Next: motion](/theme-motion/)
|
|
89
|
+
|
|
90
|
+
</div>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Motion
|
|
3
|
+
order: 5
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<div data-looma-guide>
|
|
7
|
+
|
|
8
|
+
# Motion
|
|
9
|
+
|
|
10
|
+
Set the pace and character of feedback once. Components use the shared duration and curve to make presses, openings, and other changes feel related.
|
|
11
|
+
|
|
12
|
+
## Map your decisions
|
|
13
|
+
|
|
14
|
+
::: table
|
|
15
|
+
|
|
16
|
+
- Recipe key: `response-time`
|
|
17
|
+
CSS property: `--ui-motion-fast`
|
|
18
|
+
What changes: The shared feedback duration. Other durations derive from it; the base transition duration is 1.5 times this value.
|
|
19
|
+
|
|
20
|
+
- Recipe key: `response-curve`
|
|
21
|
+
CSS property: `--ui-motion-ease`
|
|
22
|
+
What changes: How movement accelerates and settles.
|
|
23
|
+
|
|
24
|
+
:::
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"schemaVersion": 1,
|
|
29
|
+
"values": {
|
|
30
|
+
"response-time": "150ms",
|
|
31
|
+
"response-curve": "cubic-bezier(0.2,0,0.2,1)"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The CSS keeps the duration and curve as separate properties:
|
|
37
|
+
|
|
38
|
+
```css
|
|
39
|
+
:root {
|
|
40
|
+
--ui-motion-fast: 150ms;
|
|
41
|
+
--ui-motion-ease: cubic-bezier(0.2,0,0.2,1);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Compare the movement
|
|
46
|
+
|
|
47
|
+
Open **Motion** in Customize. Replay the study, then open the real popover beside it. The study uses the shared curve and travels at three times the feedback duration, giving you time to see its acceleration.
|
|
48
|
+
|
|
49
|
+
Curve handles support dragging and arrow keys, with numeric coordinates for precision. Escape or pointer cancellation restores the gesture start. Time values retain `ms` or `s`; number controls also accept arithmetic such as `150ms * 2`.
|
|
50
|
+
|
|
51
|
+
## Respect reduced motion
|
|
52
|
+
|
|
53
|
+
The exported theme includes reduced-motion rules that set shared durations to zero and neutralize the press scale. The customizer's study also respects the user's preference. When you author a component, use the shared motion roles and honor reduced motion in any additional behavior.
|
|
54
|
+
|
|
55
|
+
[Back to Theme decisions](/theme-decisions/) · [Publish your system](/publish-your-system/)
|
|
56
|
+
|
|
57
|
+
</div>
|
|
Binary file
|
|
Binary file
|
package/docs/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface DocumentationGroup { id: string; label: string }
|
|
2
|
+
export interface ExampleCode { language: string; code: string }
|
|
3
|
+
export interface DocumentationExample {
|
|
4
|
+
id: string; name: string; title: string; description: string; markup: string;
|
|
5
|
+
resizable: boolean; stretch?: boolean; frameworks: Record<string, ExampleCode>; behavior?: string; bindings?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DocumentationComponent {
|
|
8
|
+
id: string; package: string; tag: string; title: string; description: string; group: string;
|
|
9
|
+
root: string; angularHost?: string; resource: string; source?: string; importSource?: string; navigationParent?: string;
|
|
10
|
+
targets: string[]; examples: DocumentationExample[];
|
|
11
|
+
attributes: Record<string, unknown>[]; properties: Record<string, unknown>[];
|
|
12
|
+
events: Record<string, unknown>[]; slots: Record<string, unknown>[];
|
|
13
|
+
designTokens: { sources?: string[]; component: Record<string, unknown>[]; shared: Record<string, unknown>[] };
|
|
14
|
+
}
|
|
15
|
+
export interface DocumentationCatalog {
|
|
16
|
+
schemaVersion: 1; package?: string; version?: string; groups: DocumentationGroup[];
|
|
17
|
+
resources?: { tag: string; package: string; resource: string }[]; components: DocumentationComponent[];
|
|
18
|
+
}
|
|
19
|
+
export interface DiscoveryRoot { directory: string; packageName: string; groups?: DocumentationGroup[] }
|
|
20
|
+
export interface ThemeRecipe { schemaVersion: 1; values: Record<string, string>; origin?: string; fonts?: Partial<Record<'body-font' | 'display-font', string>>; presets?: Partial<Record<'material' | 'palette', string>> }
|
|
21
|
+
export interface ThemeField { family: string; id: string; label: string; property: string | null; type: string; defaultValue: string; description: string; min?: number; max?: number; choices?: Record<string, string> }
|
|
22
|
+
export interface DocumentationConfiguration {
|
|
23
|
+
title?: string; base?: string; origin?: string; outDir?: string; content?: string;
|
|
24
|
+
logo?: string | { light: string; dark?: string }; nav?: { label: string; href: string; position?: 'start' }[];
|
|
25
|
+
components?: DiscoveryRoot[]; catalogs?: string[]; categories?: Record<string, string>;
|
|
26
|
+
theme?: { recipe?: string; css?: string }; overrides?: string[];
|
|
27
|
+
recipe?: ThemeRecipe;
|
|
28
|
+
}
|
|
29
|
+
export interface DocumentationInput { root?: string; config?: DocumentationConfiguration; catalog?: DocumentationCatalog }
|
|
30
|
+
export interface DocumentationSite {
|
|
31
|
+
root: string; config: DocumentationConfiguration; base: string; catalog: DocumentationCatalog;
|
|
32
|
+
examples: (DocumentationExample & { file: string; component: string; path: string })[];
|
|
33
|
+
generated: string; content: string; previews: string; redirects: Record<string, string>;
|
|
34
|
+
}
|
|
35
|
+
export interface DocumentationBuild { outDir: string; routes: readonly string[]; browserInputs: readonly string[]; site: DocumentationSite }
|
|
36
|
+
export interface DocumentationServer { url: string; close(): Promise<void> }
|
|
37
|
+
export function readCatalog(specifier?: string | URL): Promise<DocumentationCatalog>;
|
|
38
|
+
export function discoverComponents(input: DiscoveryRoot & { root: string }): Promise<DocumentationCatalog>;
|
|
39
|
+
export function mergeCatalogs(catalogs: DocumentationCatalog[], options?: { categories?: Record<string, string> }): DocumentationCatalog;
|
|
40
|
+
export function prepareDocumentation(input: DocumentationInput & { root: string }): Promise<DocumentationSite>;
|
|
41
|
+
export function buildDocumentation(input?: DocumentationInput): Promise<DocumentationBuild>;
|
|
42
|
+
/** Optional MarkupKit integration; server tooling peers are loaded only when invoked. */
|
|
43
|
+
export function documentationOptions(site: DocumentationSite): Promise<import('@threadlabs/markupkit').ApplicationOptions>;
|
|
44
|
+
export function documentationPlugin(site: DocumentationSite): import('@threadlabs/markupkit').MarkupKitPlugin;
|
|
45
|
+
export function initializeDocumentation(input?: { root?: string; title?: string }): Promise<{ config: DocumentationConfiguration; files: string[]; applicationImport: string }>;
|
|
46
|
+
export function readConfiguration(root?: string): Promise<{ root: string; config: DocumentationConfiguration }>;
|
|
47
|
+
export function buildProjectDocumentation(input?: DocumentationInput): Promise<DocumentationBuild>;
|
|
48
|
+
export function devProjectDocumentation(input?: DocumentationInput & { port?: number; onError?: (error: unknown) => void }): Promise<DocumentationServer>;
|
|
49
|
+
export function previewProjectDocumentation(input?: DocumentationInput & { port?: number }): Promise<DocumentationServer>;
|
|
50
|
+
export const themeFields: readonly ThemeField[];
|
|
51
|
+
export const startingPoints: Record<string, ThemeRecipe>;
|
|
52
|
+
export function defaultRecipe(): ThemeRecipe;
|
|
53
|
+
export const materialPresets: Record<string, { label: string; values: Record<string, string> }>;
|
|
54
|
+
export function applyRefinement(input: ThemeRecipe, family: 'material' | 'palette', id: string): ThemeRecipe;
|
|
55
|
+
export function refinementState(input: ThemeRecipe, family: 'material' | 'palette'): { id: string; customized: boolean };
|
|
56
|
+
export function validateRecipe(input: unknown): ThemeRecipe;
|
|
57
|
+
export interface ResolvedTheme { tokens: Record<string, string>; adjustments: { id: string; requested: string; resolved: string; reason: string }[] }
|
|
58
|
+
export function deriveTheme(input: ThemeRecipe, options?: { mode?: 'light' | 'dark' }): ResolvedTheme;
|
|
59
|
+
export function themeCSS(input: ThemeRecipe, systemCSS?: string, options?: { scope?: 'portable' | 'documentation' }): string;
|
package/docs/index.mjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { readCatalog, discoverComponents, mergeCatalogs } from './catalog.mjs';
|
|
2
|
+
export { prepareDocumentation, documentationOptions, documentationPlugin, buildDocumentation } from './site.mjs';
|
|
3
|
+
export { defaultRecipe, startingPoints, themeFields, validateRecipe, deriveTheme, themeCSS, materialPresets, statusPresets, applyRefinement, refinementState } from './theme.mjs';
|
|
4
|
+
export { initializeDocumentation, buildProjectDocumentation, devProjectDocumentation, previewProjectDocumentation, readConfiguration } from './commands.mjs';
|