native-document 1.0.166 → 1.0.168
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/.vitepress/config.js +166 -0
- package/CHANGELOG.md +153 -0
- package/components.js +2 -1
- package/dist/native-document.components.min.js +495 -228
- package/dist/native-document.dev.js +7 -0
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +213 -608
- package/docs/anchor.md +173 -312
- package/docs/cache.md +95 -803
- package/docs/cli.md +179 -0
- package/docs/components/accordion.md +172 -0
- package/docs/components/alert.md +99 -0
- package/docs/components/avatar.md +160 -0
- package/docs/components/badge.md +102 -0
- package/docs/components/breadcrumb.md +89 -0
- package/docs/components/button.md +183 -0
- package/docs/components/card.md +69 -0
- package/docs/components/context-menu.md +118 -0
- package/docs/components/data-table.md +345 -0
- package/docs/components/dropdown.md +214 -0
- package/docs/components/form/autocomplete-field.md +81 -0
- package/docs/components/form/checkbox-field.md +41 -0
- package/docs/components/form/checkbox-group-field.md +54 -0
- package/docs/components/form/color-field.md +64 -0
- package/docs/components/form/date-field.md +92 -0
- package/docs/components/form/field-collection.md +63 -0
- package/docs/components/form/file-field.md +203 -0
- package/docs/components/form/form-control.md +87 -0
- package/docs/components/form/image-field.md +90 -0
- package/docs/components/form/index.md +115 -0
- package/docs/components/form/number-field.md +65 -0
- package/docs/components/form/radio-field.md +51 -0
- package/docs/components/form/select-field.md +123 -0
- package/docs/components/form/slider.md +136 -0
- package/docs/components/form/string-field.md +134 -0
- package/docs/components/form/textarea-field.md +65 -0
- package/docs/components/form-fields.md +372 -0
- package/docs/components/getting-started.md +264 -0
- package/docs/components/index.md +337 -0
- package/docs/components/layout.md +279 -0
- package/docs/components/list.md +73 -0
- package/docs/components/menu.md +215 -0
- package/docs/components/modal.md +156 -0
- package/docs/components/pagination.md +95 -0
- package/docs/components/popover.md +131 -0
- package/docs/components/progress.md +111 -0
- package/docs/components/shortcut-manager.md +221 -0
- package/docs/components/simple-table.md +107 -0
- package/docs/components/skeleton.md +155 -0
- package/docs/components/spinner.md +100 -0
- package/docs/components/splitter.md +133 -0
- package/docs/components/stepper.md +163 -0
- package/docs/components/switch.md +113 -0
- package/docs/components/tabs.md +153 -0
- package/docs/components/toast.md +119 -0
- package/docs/components/tooltip.md +151 -0
- package/docs/components/traits.md +261 -0
- package/docs/conditional-rendering.md +170 -588
- package/docs/contributing.md +300 -25
- package/docs/core-concepts.md +205 -374
- package/docs/elements.md +251 -367
- package/docs/extending-native-document-element.md +192 -207
- package/docs/filters.md +153 -1122
- package/docs/getting-started.md +193 -267
- package/docs/i18n.md +241 -0
- package/docs/index.md +76 -0
- package/docs/lifecycle-events.md +143 -75
- package/docs/list-rendering.md +227 -852
- package/docs/memory-management.md +134 -47
- package/docs/native-document-element.md +337 -186
- package/docs/native-fetch.md +99 -630
- package/docs/observable-resource.md +364 -0
- package/docs/observables.md +592 -526
- package/docs/routing.md +244 -653
- package/docs/state-management.md +134 -241
- package/docs/svg-elements.md +231 -0
- package/docs/theming.md +409 -0
- package/docs/tutorials/.gitkeep +0 -0
- package/docs/validation.md +95 -97
- package/docs/vitepress-conventions.md +219 -0
- package/package.json +34 -13
- package/readme.md +269 -89
- package/src/components/card/Card.js +93 -39
- package/src/components/card/index.js +1 -1
- package/src/components/list/HasListItem.js +171 -0
- package/src/components/list/List.js +41 -107
- package/src/components/list/ListDivider.js +39 -0
- package/src/components/list/ListGroup.js +76 -59
- package/src/components/list/ListItem.js +117 -69
- package/src/components/list/index.js +3 -1
- package/src/components/list/types/ListItem.d.ts +45 -34
- package/src/components/spacer/Spacer.js +1 -1
- package/src/core/data/ObservableResource.js +5 -0
- package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
- package/src/ui/components/card/CardRender.js +133 -0
- package/src/ui/components/card/card.css +169 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
- package/src/ui/components/list/ListRender.js +18 -0
- package/src/ui/components/list/divider/ListDividerRender.js +10 -0
- package/src/ui/components/list/divider/list-divider.css +12 -0
- package/src/ui/components/list/group/ListGroupRender.js +61 -0
- package/src/ui/components/list/group/list-group.css +62 -0
- package/src/ui/components/list/item/ListItemRender.js +238 -0
- package/src/ui/components/list/item/list-item.css +191 -0
- package/src/ui/components/list/list.css +24 -0
- package/src/ui/components/spacer/SpacerRender.js +10 -0
- package/src/ui/index.js +8 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SVG Elements
|
|
3
|
+
description: SVG wrapper functions for building reactive graphics, icons, charts, and illustrations
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SVG Elements
|
|
7
|
+
|
|
8
|
+
NativeDocument provides wrapper functions for all standard SVG elements. They follow the same API as HTML elements and support reactive attributes via observables.
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
import { SvgSvg, SvgCircle, SvgRect, SvgPath, SvgG } from 'native-document/elements';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Basic Usage
|
|
17
|
+
|
|
18
|
+
SVG elements follow the same pattern as HTML elements:
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
import { SvgSvg, SvgCircle, SvgRect, SvgLine, SvgText } from 'native-document/elements';
|
|
22
|
+
|
|
23
|
+
// Static SVG
|
|
24
|
+
const icon = SvgSvg({ width: 24, height: 24, viewBox: '0 0 24 24' }, [
|
|
25
|
+
SvgCircle({ cx: 12, cy: 12, r: 10, fill: 'none', stroke: 'currentColor', 'stroke-width': 2 })
|
|
26
|
+
]);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Reactive Attributes
|
|
32
|
+
|
|
33
|
+
Pass observables as attribute values for dynamic graphics:
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import { Observable } from 'native-document';
|
|
37
|
+
import { SvgSvg, SvgCircle, SvgRect } from 'native-document/elements';
|
|
38
|
+
|
|
39
|
+
const progress = Observable(0); // 0 to 100
|
|
40
|
+
const isActive = Observable(true);
|
|
41
|
+
const color = Observable('#3b82f6');
|
|
42
|
+
|
|
43
|
+
// Progress circle
|
|
44
|
+
const circumference = 2 * Math.PI * 45;
|
|
45
|
+
|
|
46
|
+
const ProgressRing = SvgSvg({ width: 120, height: 120, viewBox: '0 0 120 120' }, [
|
|
47
|
+
SvgCircle({
|
|
48
|
+
cx: 60, cy: 60, r: 45,
|
|
49
|
+
fill: 'none',
|
|
50
|
+
stroke: '#e5e7eb',
|
|
51
|
+
'stroke-width': 10
|
|
52
|
+
}),
|
|
53
|
+
SvgCircle({
|
|
54
|
+
cx: 60, cy: 60, r: 45,
|
|
55
|
+
fill: 'none',
|
|
56
|
+
stroke: color,
|
|
57
|
+
'stroke-width': 10,
|
|
58
|
+
'stroke-dasharray': circumference,
|
|
59
|
+
'stroke-dashoffset': progress.transform(p => circumference - (p / 100) * circumference),
|
|
60
|
+
transform: 'rotate(-90 60 60)'
|
|
61
|
+
})
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
// Updates the ring as progress changes
|
|
65
|
+
progress.set(75);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Practical Examples
|
|
71
|
+
|
|
72
|
+
### Icon component
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
import { SvgSvg, SvgPath } from 'native-document/elements';
|
|
76
|
+
|
|
77
|
+
function Icon({ path, size = 24, color = 'currentColor' }) {
|
|
78
|
+
return SvgSvg({
|
|
79
|
+
width: size,
|
|
80
|
+
height: size,
|
|
81
|
+
viewBox: '0 0 24 24',
|
|
82
|
+
fill: 'none',
|
|
83
|
+
stroke: color,
|
|
84
|
+
'stroke-width': 2,
|
|
85
|
+
'stroke-linecap': 'round',
|
|
86
|
+
'stroke-linejoin': 'round'
|
|
87
|
+
}, [
|
|
88
|
+
SvgPath({ d: path })
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Usage
|
|
93
|
+
const CheckIcon = Icon({ path: 'M20 6L9 17l-5-5' });
|
|
94
|
+
const CloseIcon = Icon({ path: 'M18 6L6 18M6 6l12 12', color: 'red' });
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Bar chart
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
import { Observable } from 'native-document';
|
|
101
|
+
import { SvgSvg, SvgRect, SvgText, SvgG, ForEach } from 'native-document/elements';
|
|
102
|
+
|
|
103
|
+
const data = Observable.array([
|
|
104
|
+
{ label: 'Jan', value: 40 },
|
|
105
|
+
{ label: 'Feb', value: 65 },
|
|
106
|
+
{ label: 'Mar', value: 55 },
|
|
107
|
+
{ label: 'Apr', value: 80 },
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
const width = 400;
|
|
111
|
+
const height = 200;
|
|
112
|
+
const padding = 40;
|
|
113
|
+
const barWidth = (width - padding * 2) / data.val().length;
|
|
114
|
+
const max = Math.max(...data.map(d => d.value));
|
|
115
|
+
|
|
116
|
+
const chart = SvgSvg({ width, height, viewBox: `0 0 ${width} ${height}` },
|
|
117
|
+
ForEach(data, (item, index) => {
|
|
118
|
+
const barHeight = (item.value / max) * (height - padding * 2);
|
|
119
|
+
const x = index.transform(i => (i * barWidth) + padding);
|
|
120
|
+
const y = height - padding - barHeight;
|
|
121
|
+
|
|
122
|
+
return SvgG([
|
|
123
|
+
SvgRect({
|
|
124
|
+
x, y,
|
|
125
|
+
width: barWidth - 4,
|
|
126
|
+
height: barHeight,
|
|
127
|
+
fill: '#3b82f6',
|
|
128
|
+
rx: 4
|
|
129
|
+
}),
|
|
130
|
+
SvgText({
|
|
131
|
+
x: x.format(v => v + (barWidth - 4) / 2),
|
|
132
|
+
y: height - padding + 16,
|
|
133
|
+
'text-anchor': 'middle',
|
|
134
|
+
'font-size': 12,
|
|
135
|
+
fill: '#6b7280'
|
|
136
|
+
}, item.label)
|
|
137
|
+
]);
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Animated loading spinner
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
import { SvgSvg, SvgCircle } from 'native-document/elements';
|
|
146
|
+
|
|
147
|
+
const Spinner = SvgSvg({
|
|
148
|
+
width: 24, height: 24,
|
|
149
|
+
viewBox: '0 0 24 24',
|
|
150
|
+
class: 'animate-spin'
|
|
151
|
+
}, [
|
|
152
|
+
SvgCircle({
|
|
153
|
+
cx: 12, cy: 12, r: 10,
|
|
154
|
+
fill: 'none',
|
|
155
|
+
stroke: '#e5e7eb',
|
|
156
|
+
'stroke-width': 3
|
|
157
|
+
}),
|
|
158
|
+
SvgCircle({
|
|
159
|
+
cx: 12, cy: 12, r: 10,
|
|
160
|
+
fill: 'none',
|
|
161
|
+
stroke: '#3b82f6',
|
|
162
|
+
'stroke-width': 3,
|
|
163
|
+
'stroke-dasharray': '31.4 62.8',
|
|
164
|
+
'stroke-linecap': 'round'
|
|
165
|
+
})
|
|
166
|
+
]);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Gradient fill
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
import { SvgSvg, SvgDefs, SvgLinearGradient, SvgStop, SvgRect } from 'native-document/elements';
|
|
173
|
+
|
|
174
|
+
const GradientBox = SvgSvg({ width: 200, height: 100, viewBox: '0 0 200 100' }, [
|
|
175
|
+
SvgDefs([
|
|
176
|
+
SvgLinearGradient({ id: 'gradient', x1: '0%', y1: '0%', x2: '100%', y2: '0%' }, [
|
|
177
|
+
SvgStop({ offset: '0%', 'stop-color': '#3b82f6' }),
|
|
178
|
+
SvgStop({ offset: '100%', 'stop-color': '#8b5cf6' })
|
|
179
|
+
])
|
|
180
|
+
]),
|
|
181
|
+
SvgRect({ width: 200, height: 100, fill: 'url(#gradient)', rx: 8 })
|
|
182
|
+
]);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Full Element Reference
|
|
188
|
+
|
|
189
|
+
### Basic shapes
|
|
190
|
+
`SvgCircle`, `SvgRect`, `SvgEllipse`, `SvgLine`, `SvgPolyline`, `SvgPolygon`, `SvgPath`
|
|
191
|
+
|
|
192
|
+
### Structure
|
|
193
|
+
`SvgSvg`, `SvgG`, `SvgDefs`, `SvgUse`, `SvgSymbol`, `SvgSwitch`, `SvgForeignObject`
|
|
194
|
+
|
|
195
|
+
### Text
|
|
196
|
+
`SvgText`, `SvgTSpan`, `SvgTextPath`
|
|
197
|
+
|
|
198
|
+
### Gradients & patterns
|
|
199
|
+
`SvgLinearGradient`, `SvgRadialGradient`, `SvgStop`, `SvgPattern`
|
|
200
|
+
|
|
201
|
+
### Clipping & masking
|
|
202
|
+
`SvgClipPath`, `SvgMask`
|
|
203
|
+
|
|
204
|
+
### Markers & images
|
|
205
|
+
`SvgMarker`, `SvgImage`
|
|
206
|
+
|
|
207
|
+
### Filters
|
|
208
|
+
`SvgFilter`, `SvgFEBlend`, `SvgFEColorMatrix`, `SvgFEComposite`, `SvgFEFlood`, `SvgFEGaussianBlur`, `SvgFEMerge`, `SvgFEMergeNode`, `SvgFEOffset`, `SvgFETurbulence`, `SvgFEDisplacementMap`, `SvgFEDiffuseLighting`, `SvgFESpecularLighting`, `SvgFEDistantLight`, `SvgFEPointLight`, `SvgFESpotLight`, `SvgFEMorphology`, `SvgFEConvolveMatrix`, `SvgFEComponentTransfer`, `SvgFEFuncR`, `SvgFEFuncG`, `SvgFEFuncB`, `SvgFEFuncA`
|
|
209
|
+
|
|
210
|
+
### Animation
|
|
211
|
+
`SvgAnimate`, `SvgAnimateTransform`, `SvgAnimateMotion`, `SvgMPath`, `SvgSet`
|
|
212
|
+
|
|
213
|
+
### Metadata
|
|
214
|
+
`SvgDesc`, `SvgTitle`, `SvgMetadata`, `SvgView`, `SvgStyle`, `SvgScript`
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Notes
|
|
219
|
+
|
|
220
|
+
- All SVG elements are created with the correct SVG namespace (`http://www.w3.org/2000/svg`) automatically
|
|
221
|
+
- Attribute names follow SVG conventions: `stroke-width`, `viewBox`, `fill-opacity`, etc.
|
|
222
|
+
- Reactive attributes work the same as HTML elements - pass an observable as the value
|
|
223
|
+
- Use `.format()` for computed string values, `.transform()` for numeric transforms
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Next Steps
|
|
228
|
+
|
|
229
|
+
- **[Elements](./elements.md)** - HTML element wrappers
|
|
230
|
+
- **[Observables](./observables.md)** - Reactive attributes
|
|
231
|
+
- **[List Rendering](./list-rendering.md)** - ForEach for dynamic chart data
|
package/docs/theming.md
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Theming
|
|
3
|
+
description: Customize NativeDocument's visual style by overriding SCSS tokens - colors, typography, spacing, radius, shadows, and animations
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Theming
|
|
7
|
+
|
|
8
|
+
NativeDocument's UI layer is built on CSS custom properties (variables). Every visual aspect of the framework - colors, typography, spacing, radius, shadows, animations - is a token that can be overridden to match your brand.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
The theme is included via the main SCSS entry point:
|
|
15
|
+
|
|
16
|
+
```scss
|
|
17
|
+
@use 'native-document/src/ui/tokens/index.scss';
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
To customize, import the index **after** defining your overrides, or create a theme file that you import instead:
|
|
21
|
+
|
|
22
|
+
```scss
|
|
23
|
+
// my-theme.scss
|
|
24
|
+
@use 'native-document/src/ui/tokens/index.scss';
|
|
25
|
+
|
|
26
|
+
:root {
|
|
27
|
+
--color-primary: #6366f1;
|
|
28
|
+
--font: 'Geist', sans-serif;
|
|
29
|
+
--radius-card: 12px;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then in your entry point:
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import './my-theme.scss';
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Colors
|
|
42
|
+
|
|
43
|
+
### Palette
|
|
44
|
+
|
|
45
|
+
Raw color values. Used by semantic tokens (see below) - override these to shift the entire palette at once.
|
|
46
|
+
|
|
47
|
+
```scss
|
|
48
|
+
:root {
|
|
49
|
+
/* Chromatic */
|
|
50
|
+
--red: #ff383c;
|
|
51
|
+
--orange: #ff8d28;
|
|
52
|
+
--yellow: #ffcc00;
|
|
53
|
+
--green: #34c759;
|
|
54
|
+
--mint: #00c8b3;
|
|
55
|
+
--teal: #00c3d0;
|
|
56
|
+
--cyan: #00c0e8;
|
|
57
|
+
--blue: #0088ff;
|
|
58
|
+
--indigo: #6155f5;
|
|
59
|
+
--purple: #cb30e0;
|
|
60
|
+
--pink: #ff2d55;
|
|
61
|
+
--brown: #ac7f5e;
|
|
62
|
+
|
|
63
|
+
/* Grays */
|
|
64
|
+
--gray: #8e8e93;
|
|
65
|
+
--gray-lite-1: #aeaeb2;
|
|
66
|
+
--gray-lite-2: #c7c7cc;
|
|
67
|
+
--gray-lite-3: #d1d1d6;
|
|
68
|
+
--gray-lite-4: #e5e5ea;
|
|
69
|
+
--gray-lite-5: #f2f2f7;
|
|
70
|
+
--white: #ffffff;
|
|
71
|
+
--black: #000000;
|
|
72
|
+
|
|
73
|
+
/* Base */
|
|
74
|
+
--background: white;
|
|
75
|
+
--background-color: white;
|
|
76
|
+
--text-color: black;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Contrasted variants
|
|
81
|
+
|
|
82
|
+
High-contrast versions of each color - used for hover states and accessible text. Override to adjust hover behavior:
|
|
83
|
+
|
|
84
|
+
```scss
|
|
85
|
+
:root {
|
|
86
|
+
--contrasted-blue: #1e6ef4;
|
|
87
|
+
--contrasted-green: #008932;
|
|
88
|
+
--contrasted-red: #e9152d;
|
|
89
|
+
--contrasted-yellow: #a16a00;
|
|
90
|
+
/* ... one for each chromatic color */
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Semantic tokens
|
|
95
|
+
|
|
96
|
+
Map palette colors to component roles. This is the recommended layer to override for branding:
|
|
97
|
+
|
|
98
|
+
```scss
|
|
99
|
+
:root {
|
|
100
|
+
--color-primary: var(--blue);
|
|
101
|
+
--color-primary-hover: var(--contrasted-blue);
|
|
102
|
+
|
|
103
|
+
--color-secondary: var(--gray-lite-4);
|
|
104
|
+
--color-secondary-hover: var(--gray-lite-3);
|
|
105
|
+
--color-secondary-text: var(--text-color);
|
|
106
|
+
|
|
107
|
+
--color-danger: var(--red);
|
|
108
|
+
--color-danger-hover: var(--contrasted-red);
|
|
109
|
+
|
|
110
|
+
--color-success: var(--green);
|
|
111
|
+
--color-success-hover: var(--contrasted-green);
|
|
112
|
+
|
|
113
|
+
--color-warning: var(--yellow);
|
|
114
|
+
--color-warning-hover: var(--contrasted-yellow);
|
|
115
|
+
|
|
116
|
+
--color-info: var(--cyan);
|
|
117
|
+
--color-info-hover: var(--contrasted-cyan);
|
|
118
|
+
|
|
119
|
+
--color-ghost: transparent;
|
|
120
|
+
--color-ghost-hover: var(--gray-lite-5);
|
|
121
|
+
--color-ghost-text: var(--text-color);
|
|
122
|
+
|
|
123
|
+
--color-link: var(--blue);
|
|
124
|
+
--color-link-hover: var(--contrasted-blue);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Dark mode
|
|
129
|
+
|
|
130
|
+
Dark mode overrides are applied automatically via `@media (prefers-color-scheme: dark)`. You can also force a mode with a class:
|
|
131
|
+
|
|
132
|
+
```html
|
|
133
|
+
<html class="dark-mode"> <!-- force dark -->
|
|
134
|
+
<html class="light-mode"> <!-- force light -->
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
To override dark mode colors:
|
|
138
|
+
|
|
139
|
+
```scss
|
|
140
|
+
@media (prefers-color-scheme: dark) {
|
|
141
|
+
:root {
|
|
142
|
+
--blue: #3b9eff;
|
|
143
|
+
--green: #30d158;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Typography
|
|
151
|
+
|
|
152
|
+
```scss
|
|
153
|
+
:root {
|
|
154
|
+
--font: Inter, Arial, sans-serif;
|
|
155
|
+
|
|
156
|
+
/* Scale */
|
|
157
|
+
--diablo-detail-size: 0.5rem; /* 8px - smallest label */
|
|
158
|
+
--diablo-size: 0.6rem; /* 10px */
|
|
159
|
+
--hint-size: 0.7rem; /* 11px - tooltips, hints */
|
|
160
|
+
--note-size: 0.8rem; /* 13px - captions */
|
|
161
|
+
--description-size: 0.9rem; /* 14px - secondary text */
|
|
162
|
+
--field-size: 0.9rem; /* 14px - form fields */
|
|
163
|
+
--text-size: 1rem; /* 16px - body */
|
|
164
|
+
--important-size: 1.25rem; /* 20px */
|
|
165
|
+
|
|
166
|
+
/* Headings */
|
|
167
|
+
--h1-font-size: 2.5rem; /* 40px */
|
|
168
|
+
--h2-font-size: 1.6rem; /* 26px */
|
|
169
|
+
--h3-font-size: 1.4rem; /* 22px */
|
|
170
|
+
--h4-font-size: 1.5rem; /* 24px */
|
|
171
|
+
--h5-font-size: 1.25rem; /* 20px */
|
|
172
|
+
--h6-font-size: 1.15rem; /* 18px */
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Custom font example
|
|
177
|
+
|
|
178
|
+
```scss
|
|
179
|
+
@import url('https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600&display=swap');
|
|
180
|
+
|
|
181
|
+
:root {
|
|
182
|
+
--font: 'Geist', sans-serif;
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Spacing
|
|
189
|
+
|
|
190
|
+
8 named steps from `tiny` (4px) to `spacious` (48px):
|
|
191
|
+
|
|
192
|
+
```scss
|
|
193
|
+
:root {
|
|
194
|
+
--space-tiny: 0.25rem; /* 4px */
|
|
195
|
+
--space-tight: 0.35rem; /* 6px */
|
|
196
|
+
--space-cozy: 0.5rem; /* 8px */
|
|
197
|
+
--space-cozy-comfortable: 0.75rem; /* 12px */
|
|
198
|
+
--space-comfortable: 1rem; /* 16px */
|
|
199
|
+
--space-relaxed: 1.5rem; /* 24px */
|
|
200
|
+
--space-loose: 2rem; /* 32px */
|
|
201
|
+
--space-spacious: 3rem; /* 48px */
|
|
202
|
+
|
|
203
|
+
--container-padding: 1rem;
|
|
204
|
+
--container-margin: 0rem;
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Border radius
|
|
211
|
+
|
|
212
|
+
```scss
|
|
213
|
+
:root {
|
|
214
|
+
--radius-none: 0;
|
|
215
|
+
--radius-small: 0.25rem; /* 4px */
|
|
216
|
+
--radius-medium: 0.5rem; /* 8px */
|
|
217
|
+
--radius-large: 1rem; /* 16px */
|
|
218
|
+
--radius-round: 50%;
|
|
219
|
+
|
|
220
|
+
/* Component-specific */
|
|
221
|
+
--radius-button: 5px;
|
|
222
|
+
--radius-card: 5px;
|
|
223
|
+
--radius-modal: 12px;
|
|
224
|
+
--radius-pill: 35px;
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
To round all buttons and cards:
|
|
229
|
+
|
|
230
|
+
```scss
|
|
231
|
+
:root {
|
|
232
|
+
--radius-button: 8px;
|
|
233
|
+
--radius-card: 12px;
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Shadows
|
|
240
|
+
|
|
241
|
+
```scss
|
|
242
|
+
:root {
|
|
243
|
+
/* Scale */
|
|
244
|
+
--shadow-none: none;
|
|
245
|
+
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
246
|
+
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
247
|
+
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
|
|
248
|
+
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
249
|
+
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04);
|
|
250
|
+
--shadow-double-xl: 0 25px 50px rgba(0, 0, 0, 0.25);
|
|
251
|
+
|
|
252
|
+
/* Component-specific */
|
|
253
|
+
--shadow-card: 0 4px 6px rgba(0, 0, 0, 0.07);
|
|
254
|
+
--shadow-button: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
255
|
+
--shadow-modal: 0 20px 25px rgba(0, 0, 0, 0.15);
|
|
256
|
+
--shadow-dropdown: 0 10px 15px rgba(0, 0, 0, 0.1);
|
|
257
|
+
--shadow-tooltip: 0 4px 8px rgba(0, 0, 0, 0.12);
|
|
258
|
+
|
|
259
|
+
/* Interactive */
|
|
260
|
+
--shadow-focus: 0 0 0 3px var(--shadow-focus-color);
|
|
261
|
+
--shadow-outline: 0 0 0 3px var(--shadow-outline-color);
|
|
262
|
+
--shadow-glow: 0 0 20px var(--shadow-glow-color);
|
|
263
|
+
--shadow-inner: inset 0 2px 4px var(--shadow-inner-color);
|
|
264
|
+
|
|
265
|
+
/* Focus ring color */
|
|
266
|
+
--shadow-focus-color: rgba(59, 130, 246, 0.5);
|
|
267
|
+
--shadow-outline-color: rgba(59, 130, 246, 0.5);
|
|
268
|
+
--shadow-glow-color: rgba(59, 130, 246, 0.3);
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
To change the focus ring color to match your brand:
|
|
273
|
+
|
|
274
|
+
```scss
|
|
275
|
+
:root {
|
|
276
|
+
--shadow-focus-color: rgba(99, 102, 241, 0.5);
|
|
277
|
+
--shadow-outline-color: rgba(99, 102, 241, 0.5);
|
|
278
|
+
--shadow-glow-color: rgba(99, 102, 241, 0.3);
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Animation
|
|
285
|
+
|
|
286
|
+
```scss
|
|
287
|
+
:root {
|
|
288
|
+
/* Durations */
|
|
289
|
+
--fast: 0.2s;
|
|
290
|
+
--natural: 0.3s;
|
|
291
|
+
--medium: 0.5s;
|
|
292
|
+
--slow: 1s;
|
|
293
|
+
--very-slow: 5s;
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
To slow down all transitions (useful for accessibility or debugging):
|
|
298
|
+
|
|
299
|
+
```scss
|
|
300
|
+
:root {
|
|
301
|
+
--fast: 0.3s;
|
|
302
|
+
--natural: 0.5s;
|
|
303
|
+
--medium: 0.8s;
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Opacity
|
|
310
|
+
|
|
311
|
+
```scss
|
|
312
|
+
:root {
|
|
313
|
+
--opacity-disabled: 0.5;
|
|
314
|
+
--opacity-backdrop: 0.7;
|
|
315
|
+
--opacity-ghost: 0.1;
|
|
316
|
+
--opacity-faint: 0.2;
|
|
317
|
+
--opacity-subtle: 0.3;
|
|
318
|
+
/* ... full scale from 0.1 to 1 */
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Z-index layers
|
|
325
|
+
|
|
326
|
+
```scss
|
|
327
|
+
:root {
|
|
328
|
+
--floor-layer: 0;
|
|
329
|
+
--tooltip-layer: 2;
|
|
330
|
+
--dropdown-layer: 4;
|
|
331
|
+
--overlay-layer: 10000;
|
|
332
|
+
--modal-layer: 10001;
|
|
333
|
+
--z-stack-z-index: 10000;
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Breakpoints
|
|
340
|
+
|
|
341
|
+
```scss
|
|
342
|
+
:root {
|
|
343
|
+
--mobile-width: 320px;
|
|
344
|
+
--tablet-width: 768px;
|
|
345
|
+
--desktop-width: 1024px;
|
|
346
|
+
--wide-desktop-width: 1280px;
|
|
347
|
+
--fullhd-width: 1440px;
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Utility classes applied automatically:
|
|
352
|
+
|
|
353
|
+
| Class | Visible on |
|
|
354
|
+
|---|---|
|
|
355
|
+
| `.mobile-only` | Mobile only (hidden above 768px) |
|
|
356
|
+
| `.desktop-only` | Desktop only (hidden below 768px) |
|
|
357
|
+
| `.at-least-tablet` | Tablet and above |
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Complete theme example
|
|
362
|
+
|
|
363
|
+
```scss
|
|
364
|
+
// themes/brand.scss
|
|
365
|
+
@use 'native-document/src/ui/tokens/index.scss';
|
|
366
|
+
|
|
367
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
|
368
|
+
|
|
369
|
+
:root {
|
|
370
|
+
/* Brand colors */
|
|
371
|
+
--blue: #6366f1;
|
|
372
|
+
--contrasted-blue: #4f46e5;
|
|
373
|
+
|
|
374
|
+
/* Semantic */
|
|
375
|
+
--color-primary: var(--blue);
|
|
376
|
+
--color-primary-hover: var(--contrasted-blue);
|
|
377
|
+
|
|
378
|
+
/* Focus ring matches brand */
|
|
379
|
+
--shadow-focus-color: rgba(99, 102, 241, 0.5);
|
|
380
|
+
--shadow-outline-color: rgba(99, 102, 241, 0.5);
|
|
381
|
+
--shadow-glow-color: rgba(99, 102, 241, 0.3);
|
|
382
|
+
|
|
383
|
+
/* Typography */
|
|
384
|
+
--font: 'Inter', sans-serif;
|
|
385
|
+
|
|
386
|
+
/* Rounder corners */
|
|
387
|
+
--radius-button: 8px;
|
|
388
|
+
--radius-card: 12px;
|
|
389
|
+
--radius-modal: 16px;
|
|
390
|
+
|
|
391
|
+
/* Slightly slower animations */
|
|
392
|
+
--fast: 0.25s;
|
|
393
|
+
--natural: 0.35s;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
@media (prefers-color-scheme: dark) {
|
|
397
|
+
:root {
|
|
398
|
+
--blue: #818cf8;
|
|
399
|
+
--contrasted-blue: #6366f1;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Next Steps
|
|
407
|
+
|
|
408
|
+
- **[Components Overview](./components/index.md)** - BaseComponent and renderer pattern
|
|
409
|
+
- **[Getting Started](./components/getting-started.md)** - Register default renderers
|
|
File without changes
|