orbiq-neural-ui-kit 0.0.1
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 +64 -0
- package/fesm2022/orbiq-neural-ui-kit.mjs +13521 -0
- package/fesm2022/orbiq-neural-ui-kit.mjs.map +1 -0
- package/package.json +45 -0
- package/src/theme.css +6 -0
- package/tailwind/colorPalette.js +193 -0
- package/tailwind/colors.json +1173 -0
- package/tailwind/content.js +58 -0
- package/tailwind/figma-tokens-to-theme.js +471 -0
- package/tailwind/figma-tokens-to-theme.test.js +22 -0
- package/tailwind/generated/colors.json +1173 -0
- package/tailwind/generated/effects.json +39 -0
- package/tailwind/generated/radius.json +14 -0
- package/tailwind/generated/typography.json +402 -0
- package/tailwind/iconPackPlugin.js +108 -0
- package/tailwind/index.js +2 -0
- package/tailwind/lucideIconsPlugin.js +23 -0
- package/tailwind/migrate-tokens-v2.js +938 -0
- package/tailwind/migrate-tokens-v2.test.js +528 -0
- package/tailwind/plugin.js +693 -0
- package/tailwind/preset.js +36 -0
- package/types/orbiq-neural-ui-kit.d.ts +1892 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
import plugin from 'tailwindcss/plugin'
|
|
2
|
+
import {
|
|
3
|
+
generateColorPalette,
|
|
4
|
+
generateSemanticColors,
|
|
5
|
+
generateCSSVariables,
|
|
6
|
+
generateEffectVariables,
|
|
7
|
+
} from './colorPalette.js'
|
|
8
|
+
import radiusTokens from './generated/radius.json'
|
|
9
|
+
import typographyTokens from './generated/typography.json'
|
|
10
|
+
import effectsData from './generated/effects.json'
|
|
11
|
+
|
|
12
|
+
let colorPalette = generateColorPalette()
|
|
13
|
+
let semanticColors = generateSemanticColors()
|
|
14
|
+
let cssVariables = mergeVariableLayers(
|
|
15
|
+
generateCSSVariables(),
|
|
16
|
+
generateEffectVariables(),
|
|
17
|
+
generateRadiusVariables(),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
// Emit `--radius-{key}` for every radius token (the numbered scale plus the
|
|
21
|
+
// kept `none` / `full` names — the deprecated size aliases were removed in
|
|
22
|
+
// 1.0.0 per ADR-0006, #998) so the values are inspectable as real CSS
|
|
23
|
+
// variables. `borderRadius` is rewired below to consume these vars, so
|
|
24
|
+
// `rounded-4` and `--radius-4` stay in sync.
|
|
25
|
+
function generateRadiusVariables() {
|
|
26
|
+
const vars = {}
|
|
27
|
+
for (const [key, value] of Object.entries(radiusTokens)) {
|
|
28
|
+
vars[`--radius-${key}`] = value
|
|
29
|
+
}
|
|
30
|
+
return { ':root': vars }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Each value carries a trailing `/* {px} */` comment so editor tooling
|
|
34
|
+
// (Tailwind IntelliSense) surfaces the resolved px on hover, instead of
|
|
35
|
+
// the opaque `var(--radius-*)` reference. No `DEFAULT` key: the bare
|
|
36
|
+
// `rounded` utility no longer exists — use `rounded-4`.
|
|
37
|
+
function buildRadiusConfig() {
|
|
38
|
+
const out = {}
|
|
39
|
+
for (const [key, value] of Object.entries(radiusTokens)) {
|
|
40
|
+
out[key] = `var(--radius-${key}) /* ${value} */`
|
|
41
|
+
}
|
|
42
|
+
return out
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Merge two `{ selector: { var: value } }` objects into one, preserving any
|
|
46
|
+
// vars already declared under the same selector.
|
|
47
|
+
function mergeVariableLayers(...layers) {
|
|
48
|
+
const out = {}
|
|
49
|
+
for (const layer of layers) {
|
|
50
|
+
for (const [selector, vars] of Object.entries(layer)) {
|
|
51
|
+
out[selector] = { ...(out[selector] || {}), ...vars }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Weight variants are exposed as component classes — `text-<size>-<weight>` and
|
|
58
|
+
// the paragraph counterpart `text-p-<size>-<weight>` — one per Figma named
|
|
59
|
+
// style. Figma tracks each weight differently per size and CSS letter-spacing
|
|
60
|
+
// can't follow font-weight, so each (size, weight) must ship as a self-contained
|
|
61
|
+
// class. `regular` stays the bare `text-<size>` / `text-p-<size>` utility.
|
|
62
|
+
// (Component classes are JIT-purged, so only the ones used in content emit.)
|
|
63
|
+
// `black` was dropped in #998 — zero usage, and the Figma weights behind it
|
|
64
|
+
// were corrupt export data.
|
|
65
|
+
const WEIGHT_VARIANTS = ['medium', 'semibold', 'bold']
|
|
66
|
+
|
|
67
|
+
function buildFontSize() {
|
|
68
|
+
const out = {}
|
|
69
|
+
// Each size's regular variant already carries lineHeight, letterSpacing and
|
|
70
|
+
// fontWeight from the text-styles export (see figma-tokens-to-theme.js).
|
|
71
|
+
for (const [key, [size, meta]] of Object.entries(typographyTokens.fontSize)) {
|
|
72
|
+
out[key] = [size, { ...meta }]
|
|
73
|
+
}
|
|
74
|
+
// Paragraph variants (`text-p-<size>`): same size, the paragraph style's
|
|
75
|
+
// looser line-height and its own letter-spacing.
|
|
76
|
+
for (const [key, p] of Object.entries(typographyTokens.paragraph || {})) {
|
|
77
|
+
if (!out[key]) continue
|
|
78
|
+
const [size, meta] = out[key]
|
|
79
|
+
out[`p-${key}`] = [
|
|
80
|
+
size,
|
|
81
|
+
{ ...meta, lineHeight: p.lineHeight, letterSpacing: p.letterSpacing },
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
return out
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Focus ring utilities backed by `--focus-outline-*` CSS vars (theme-flipped
|
|
88
|
+
// in colorPalette.js#generateEffectVariables). Implemented as `outline`, not
|
|
89
|
+
// box-shadow, so rings never collide with shadow/ring utilities on the same
|
|
90
|
+
// element and survive forced-colors mode. The default ring is applied
|
|
91
|
+
// globally via `:focus-visible` (see globalStyles); these utilities are for
|
|
92
|
+
// themed overrides (`focus-visible:focus-ring-red`) and non-focus states
|
|
93
|
+
// (`data-[state=open]:focus-ring`). Registered via `addComponents` so
|
|
94
|
+
// Tailwind IntelliSense picks them up.
|
|
95
|
+
function buildFocusRingUtilities() {
|
|
96
|
+
const out = {}
|
|
97
|
+
for (const name of Object.keys(effectsData.focus.light)) {
|
|
98
|
+
const className = name === 'default' ? '.focus-ring' : `.focus-ring-${name}`
|
|
99
|
+
out[className] = {
|
|
100
|
+
outline: `var(--focus-outline-${name})`,
|
|
101
|
+
outlineOffset: '0px',
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return out
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildTextStyleUtilities() {
|
|
108
|
+
const out = {}
|
|
109
|
+
const t = typographyTokens
|
|
110
|
+
const groups = [
|
|
111
|
+
{
|
|
112
|
+
className: (s, w) => `.text-${s}-${w}`,
|
|
113
|
+
tracking: t.tracking?.text || {},
|
|
114
|
+
lineHeight: (s) => t.fontSize[s]?.[1].lineHeight,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
className: (s, w) => `.text-p-${s}-${w}`,
|
|
118
|
+
tracking: t.tracking?.paragraph || {},
|
|
119
|
+
lineHeight: (s) => t.paragraph?.[s]?.lineHeight,
|
|
120
|
+
},
|
|
121
|
+
]
|
|
122
|
+
for (const group of groups) {
|
|
123
|
+
for (const [size, byWeight] of Object.entries(group.tracking)) {
|
|
124
|
+
const entry = t.fontSize[size]
|
|
125
|
+
if (!entry) continue
|
|
126
|
+
const [fontSize] = entry
|
|
127
|
+
const lineHeight = group.lineHeight(size)
|
|
128
|
+
const transform = t.textTransform?.[size]
|
|
129
|
+
for (const weight of WEIGHT_VARIANTS) {
|
|
130
|
+
if (!(weight in byWeight)) continue
|
|
131
|
+
out[group.className(size, weight)] = {
|
|
132
|
+
fontSize,
|
|
133
|
+
lineHeight,
|
|
134
|
+
fontWeight: String(t.fontWeight[weight]),
|
|
135
|
+
letterSpacing: byWeight[weight],
|
|
136
|
+
...(transform ? { textTransform: transform } : {}),
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// `tiny` is an uppercase eyebrow style; the bare regular utility needs the
|
|
142
|
+
// text-transform too (Tailwind's fontSize tuple can't express it).
|
|
143
|
+
for (const [size, transform] of Object.entries(t.textTransform || {})) {
|
|
144
|
+
out[`.text-${size}`] = {
|
|
145
|
+
...(out[`.text-${size}`] || {}),
|
|
146
|
+
textTransform: transform,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return out
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let globalStyles = (theme) => ({
|
|
153
|
+
html: {
|
|
154
|
+
'font-family': `InterVar, ${theme('fontFamily.sans')}`,
|
|
155
|
+
'font-optical-sizing': 'auto',
|
|
156
|
+
},
|
|
157
|
+
'html, body, button, p, span, div': {
|
|
158
|
+
fontVariationSettings: "'opsz' 24, 'cv11' 1",
|
|
159
|
+
WebkitFontSmoothing: 'antialiased',
|
|
160
|
+
MozOsxFontSmoothing: 'grayscale',
|
|
161
|
+
},
|
|
162
|
+
select: {
|
|
163
|
+
backgroundImage:
|
|
164
|
+
'url(\'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="%237C7C7C" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" aria-hidden="true" viewBox="0 0 24 24" ><path d="m6 9 6 6 6-6" /></svg>\')',
|
|
165
|
+
backgroundSize: '1.13em',
|
|
166
|
+
backgroundPosition: 'right 0.44rem center',
|
|
167
|
+
},
|
|
168
|
+
// A bare `<p>` reads as body copy, so it defaults to a relaxed line-height
|
|
169
|
+
// instead of the tight value baked into the `text-<size>` utilities. Kept at
|
|
170
|
+
// element specificity (0,0,1) with `:where()` (which adds none) so any explicit
|
|
171
|
+
// `text-*` / `text-p-*` line-height wins, and scoped out of rich text so the
|
|
172
|
+
// `prose` / editor line-heights are untouched.
|
|
173
|
+
'p:not(:where(.prose, .ProseMirror) *)': {
|
|
174
|
+
lineHeight: '1.5',
|
|
175
|
+
},
|
|
176
|
+
// Global keyboard focus indicator (espresso v2 focus/default token).
|
|
177
|
+
// Lives in the base layer so any utility on the element can override it:
|
|
178
|
+
// suppress with `focus-visible:outline-none`, retheme with
|
|
179
|
+
// `focus-visible:focus-ring-<color>`.
|
|
180
|
+
':focus-visible': {
|
|
181
|
+
outline: 'var(--focus-outline-default)',
|
|
182
|
+
outlineOffset: '0px',
|
|
183
|
+
},
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
let componentStyles = {
|
|
187
|
+
'.form-input, .form-textarea, .form-select': {
|
|
188
|
+
'@apply h-7 rounded-4 border border-[--surface-gray-2] bg-surface-gray-2 py-1.5 pl-2 pr-2 text-base text-ink-gray-8 placeholder-ink-gray-4 transition-colors hover:border-outline-elevation-2 hover:bg-surface-gray-3 focus:border-outline-gray-4 focus:bg-surface-base focus:shadow-sm focus:ring-0':
|
|
189
|
+
{},
|
|
190
|
+
},
|
|
191
|
+
'.form-checkbox': {
|
|
192
|
+
'@apply rounded-5 bg-surface-gray-2 text-ink-blue-4 focus:ring-0': {},
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let extraBaseStyles = {
|
|
197
|
+
"[data-theme='dark'] [type='checkbox']:checked": {
|
|
198
|
+
'background-image': `url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='%230F0F0F' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")`,
|
|
199
|
+
},
|
|
200
|
+
"[data-theme='dark'] [type='checkbox']:indeterminate": {
|
|
201
|
+
'background-image': `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='%230F0F0F' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e")`,
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default plugin(
|
|
206
|
+
function ({ addBase, addComponents, matchUtilities, theme }) {
|
|
207
|
+
addBase({ ...globalStyles(theme), ...cssVariables, ...extraBaseStyles })
|
|
208
|
+
addComponents(componentStyles)
|
|
209
|
+
addComponents(buildTextStyleUtilities())
|
|
210
|
+
addComponents(buildFocusRingUtilities())
|
|
211
|
+
// Authoring sugar for the list-family styling hooks (frappe-ui/list),
|
|
212
|
+
// on the spacing scale with variants and arbitrary values:
|
|
213
|
+
// `max-sm:list-gap-3` instead of `max-sm:[--list-gap:0.75rem]`.
|
|
214
|
+
matchUtilities(
|
|
215
|
+
{
|
|
216
|
+
'list-gap': (value) => ({ '--list-gap': value }),
|
|
217
|
+
'list-row-px': (value) => ({ '--list-row-padding-x': value }),
|
|
218
|
+
},
|
|
219
|
+
{ values: theme('spacing') },
|
|
220
|
+
)
|
|
221
|
+
// Grid tracks are arbitrary-only (no meaningful scale):
|
|
222
|
+
// `max-md:list-cols-[minmax(0,1fr)_auto]` instead of
|
|
223
|
+
// `max-md:[--list-columns:minmax(0,1fr)_auto]`.
|
|
224
|
+
matchUtilities({
|
|
225
|
+
'list-cols': (value) => ({ '--list-columns': value }),
|
|
226
|
+
})
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
theme: {
|
|
230
|
+
colors: colorPalette,
|
|
231
|
+
borderRadius: buildRadiusConfig(),
|
|
232
|
+
boxShadow: {
|
|
233
|
+
none: 'none',
|
|
234
|
+
sm: 'var(--elevation-sm)',
|
|
235
|
+
base: 'var(--elevation-base)',
|
|
236
|
+
DEFAULT: 'var(--elevation-base)',
|
|
237
|
+
md: 'var(--elevation-md)',
|
|
238
|
+
lg: 'var(--elevation-lg)',
|
|
239
|
+
xl: 'var(--elevation-xl)',
|
|
240
|
+
'2xl': 'var(--elevation-2xl)',
|
|
241
|
+
},
|
|
242
|
+
container: {
|
|
243
|
+
padding: {
|
|
244
|
+
xl: '5rem',
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
fontSize: buildFontSize(),
|
|
248
|
+
screens: {
|
|
249
|
+
sm: '640px',
|
|
250
|
+
md: '768px',
|
|
251
|
+
lg: '1024px',
|
|
252
|
+
xl: '1280px',
|
|
253
|
+
},
|
|
254
|
+
extend: {
|
|
255
|
+
textColor: {
|
|
256
|
+
ink: semanticColors.ink,
|
|
257
|
+
},
|
|
258
|
+
backgroundColor: {
|
|
259
|
+
surface: semanticColors.surface,
|
|
260
|
+
'surface-alpha': semanticColors['surface-alpha'],
|
|
261
|
+
},
|
|
262
|
+
gradientColorStops: {
|
|
263
|
+
surface: semanticColors.surface,
|
|
264
|
+
'surface-alpha': semanticColors['surface-alpha'],
|
|
265
|
+
ink: semanticColors.ink,
|
|
266
|
+
outline: semanticColors.outline,
|
|
267
|
+
'outline-alpha': semanticColors['outline-alpha'],
|
|
268
|
+
},
|
|
269
|
+
fill: {
|
|
270
|
+
ink: semanticColors.ink,
|
|
271
|
+
surface: semanticColors.surface,
|
|
272
|
+
},
|
|
273
|
+
stroke: {
|
|
274
|
+
ink: semanticColors.ink,
|
|
275
|
+
},
|
|
276
|
+
placeholderColor: {
|
|
277
|
+
ink: semanticColors.ink,
|
|
278
|
+
},
|
|
279
|
+
borderColor: () => ({
|
|
280
|
+
DEFAULT: 'var(--outline-gray-1)',
|
|
281
|
+
outline: semanticColors.outline,
|
|
282
|
+
'outline-alpha': semanticColors['outline-alpha'],
|
|
283
|
+
}),
|
|
284
|
+
ringColor: {
|
|
285
|
+
outline: semanticColors.outline,
|
|
286
|
+
'outline-alpha': semanticColors['outline-alpha'],
|
|
287
|
+
},
|
|
288
|
+
divideColor: {
|
|
289
|
+
outline: semanticColors.outline,
|
|
290
|
+
'outline-alpha': semanticColors['outline-alpha'],
|
|
291
|
+
},
|
|
292
|
+
spacing: {
|
|
293
|
+
4.5: '1.125rem',
|
|
294
|
+
5.5: '1.375rem',
|
|
295
|
+
6.5: '1.625rem',
|
|
296
|
+
7.5: '1.875rem',
|
|
297
|
+
8.5: '2.125rem',
|
|
298
|
+
9.5: '2.375rem',
|
|
299
|
+
10.5: '2.625rem',
|
|
300
|
+
11.5: '2.875rem',
|
|
301
|
+
12.5: '3.125rem',
|
|
302
|
+
13: '3.25rem',
|
|
303
|
+
13.5: '3.375rem',
|
|
304
|
+
14.5: '3.625rem',
|
|
305
|
+
15: '3.75rem',
|
|
306
|
+
15.5: '3.875rem',
|
|
307
|
+
},
|
|
308
|
+
width: {
|
|
309
|
+
3.5: '0.875rem',
|
|
310
|
+
112: '28rem',
|
|
311
|
+
wizard: '650px',
|
|
312
|
+
},
|
|
313
|
+
height: {
|
|
314
|
+
3.5: '0.875rem',
|
|
315
|
+
},
|
|
316
|
+
minWidth: {
|
|
317
|
+
40: '10rem',
|
|
318
|
+
50: '18rem',
|
|
319
|
+
},
|
|
320
|
+
maxHeight: {
|
|
321
|
+
52: '13rem',
|
|
322
|
+
},
|
|
323
|
+
typography: (theme) => ({
|
|
324
|
+
DEFAULT: {
|
|
325
|
+
css: {
|
|
326
|
+
'--tw-prose-body': 'var(--ink-gray-8)',
|
|
327
|
+
'--tw-prose-headings': 'var(--ink-gray-9)',
|
|
328
|
+
'--tw-prose-lead': 'var(--ink-gray-5)',
|
|
329
|
+
'--tw-prose-links': 'var(--ink-gray-9)',
|
|
330
|
+
'--tw-prose-bold': 'var(--ink-gray-9)',
|
|
331
|
+
'--tw-prose-counters': 'var(--ink-gray-4)',
|
|
332
|
+
'--tw-prose-bullets': 'var(--ink-gray-2)',
|
|
333
|
+
'--tw-prose-hr': 'var(--ink-gray-1)',
|
|
334
|
+
'--tw-prose-quotes': 'var(--ink-gray-8)',
|
|
335
|
+
'--tw-prose-quote-borders': 'var(--ink-gray-1)',
|
|
336
|
+
'--tw-prose-captions': 'var(--ink-gray-4)',
|
|
337
|
+
'--tw-prose-kbd': 'var(--ink-gray-9)',
|
|
338
|
+
'--tw-prose-code': 'var(--ink-gray-9)',
|
|
339
|
+
'--tw-prose-pre-code': 'var(--ink-gray-1)',
|
|
340
|
+
'--tw-prose-pre-bg': 'var(--ink-gray-8)',
|
|
341
|
+
'--tw-prose-th-borders': 'var(--ink-gray-2)',
|
|
342
|
+
'--tw-prose-td-borders': 'var(--ink-gray-1)',
|
|
343
|
+
h1: {
|
|
344
|
+
fontWeight: 600,
|
|
345
|
+
},
|
|
346
|
+
h2: {
|
|
347
|
+
fontWeight: 600,
|
|
348
|
+
},
|
|
349
|
+
h3: {
|
|
350
|
+
fontWeight: 600,
|
|
351
|
+
},
|
|
352
|
+
h4: {
|
|
353
|
+
fontWeight: 600,
|
|
354
|
+
},
|
|
355
|
+
h5: {
|
|
356
|
+
fontWeight: 600,
|
|
357
|
+
},
|
|
358
|
+
'h1 strong': {
|
|
359
|
+
fontWeight: 600,
|
|
360
|
+
},
|
|
361
|
+
'h2 strong': {
|
|
362
|
+
fontWeight: 600,
|
|
363
|
+
},
|
|
364
|
+
'h3 strong': {
|
|
365
|
+
fontWeight: 600,
|
|
366
|
+
},
|
|
367
|
+
'h4 strong': {
|
|
368
|
+
fontWeight: 600,
|
|
369
|
+
},
|
|
370
|
+
'h5 strong': {
|
|
371
|
+
fontWeight: 600,
|
|
372
|
+
},
|
|
373
|
+
'img[data-align=right]': {
|
|
374
|
+
marginLeft: 'auto',
|
|
375
|
+
marginRight: '0',
|
|
376
|
+
},
|
|
377
|
+
'img[data-align=center]': {
|
|
378
|
+
marginLeft: 'auto',
|
|
379
|
+
marginRight: 'auto',
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
sm: {
|
|
384
|
+
css: {
|
|
385
|
+
fontSize: '14px',
|
|
386
|
+
fontWeight: 420,
|
|
387
|
+
lineHeight: 1.5,
|
|
388
|
+
letterSpacing: '0.02em',
|
|
389
|
+
h1: {
|
|
390
|
+
fontSize: em(20, 14),
|
|
391
|
+
},
|
|
392
|
+
h2: {
|
|
393
|
+
fontSize: em(18, 14),
|
|
394
|
+
},
|
|
395
|
+
h3: {
|
|
396
|
+
fontSize: em(16, 14),
|
|
397
|
+
},
|
|
398
|
+
h4: {
|
|
399
|
+
fontSize: em(14, 14),
|
|
400
|
+
},
|
|
401
|
+
h5: {
|
|
402
|
+
fontSize: em(13, 14),
|
|
403
|
+
},
|
|
404
|
+
p: {
|
|
405
|
+
marginTop: '0.5rem',
|
|
406
|
+
marginBottom: '0.5rem',
|
|
407
|
+
},
|
|
408
|
+
'ul > li': {
|
|
409
|
+
margin: '0.5rem 0',
|
|
410
|
+
'> p': {
|
|
411
|
+
margin: '0.5rem 0',
|
|
412
|
+
},
|
|
413
|
+
'> p:first-child:last-child': {
|
|
414
|
+
margin: '0.5rem 0',
|
|
415
|
+
},
|
|
416
|
+
'> p:first-child': {
|
|
417
|
+
marginTop: '0.5rem',
|
|
418
|
+
},
|
|
419
|
+
'> p:last-child': {
|
|
420
|
+
marginBottom: '0.5rem',
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
'ol > li': {
|
|
424
|
+
margin: '0.5rem 0',
|
|
425
|
+
'> p': {
|
|
426
|
+
margin: '0.5rem 0',
|
|
427
|
+
},
|
|
428
|
+
'> p:first-child:last-child': {
|
|
429
|
+
margin: '0.5rem 0',
|
|
430
|
+
},
|
|
431
|
+
'> p:first-child': {
|
|
432
|
+
marginTop: '0.5rem',
|
|
433
|
+
},
|
|
434
|
+
'> p:last-child': {
|
|
435
|
+
marginBottom: '0.5rem',
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
// prose-v3: zero paragraph margins, user controls spacing with Enter
|
|
441
|
+
// all spacing on 8px grid: 4, 8, 16, 24, 32px
|
|
442
|
+
// empty <p> = 14px × 1.7 line-height ≈ 23.8px (the user's spacing unit)
|
|
443
|
+
//
|
|
444
|
+
// Base font-size is customizable via `--prose-font-size` (default 14px).
|
|
445
|
+
// Every child size is `em`-relative to this base, so overriding the
|
|
446
|
+
// variable rescales the whole editor proportionally — headings, lists,
|
|
447
|
+
// code — while line-height (unitless) and em letter-spacing scale too.
|
|
448
|
+
// Set it with an arbitrary-property utility or inline style, e.g.
|
|
449
|
+
// <EditorContent class="[--prose-font-size:1rem]" />
|
|
450
|
+
v3: {
|
|
451
|
+
css: [
|
|
452
|
+
{
|
|
453
|
+
fontSize: 'var(--prose-font-size, 15px)',
|
|
454
|
+
fontWeight: 420,
|
|
455
|
+
lineHeight: '1.7',
|
|
456
|
+
letterSpacing: '0.02em',
|
|
457
|
+
|
|
458
|
+
// prose-v3 color tokens — calmer, softer than defaults
|
|
459
|
+
'--tw-prose-body': 'var(--ink-gray-7)',
|
|
460
|
+
'--tw-prose-bold': 'var(--ink-gray-8)',
|
|
461
|
+
'--tw-prose-quotes': 'var(--ink-gray-7)',
|
|
462
|
+
'--tw-prose-quote-borders': 'var(--ink-gray-3)',
|
|
463
|
+
'--tw-prose-kbd': 'var(--ink-gray-8)',
|
|
464
|
+
'--tw-prose-code': 'var(--ink-gray-8)',
|
|
465
|
+
|
|
466
|
+
// links: subtle bottom border, darkens on hover
|
|
467
|
+
a: {
|
|
468
|
+
textDecoration: 'none',
|
|
469
|
+
borderBottom: '1px solid var(--ink-gray-4)',
|
|
470
|
+
transition: 'border-color 0.08s ease',
|
|
471
|
+
},
|
|
472
|
+
'a:hover': {
|
|
473
|
+
borderBottom: '1px solid var(--ink-gray-6)',
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
// named text color + bold: the color lives on a `textStyle`
|
|
477
|
+
// span that wraps (or is wrapped by) `<strong>`. Typography's
|
|
478
|
+
// `strong { color: var(--tw-prose-bold) }` otherwise overrides
|
|
479
|
+
// the inherited named color, painting bold text gray. Mirror the
|
|
480
|
+
// base preset's `a strong { color: inherit }` so bold keeps the
|
|
481
|
+
// span's color in both nesting orders.
|
|
482
|
+
':where(span[style*="--prose-color-"]) strong': {
|
|
483
|
+
color: 'inherit',
|
|
484
|
+
},
|
|
485
|
+
'strong:where([style*="--prose-color-"])': {
|
|
486
|
+
color: 'inherit',
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
// inline code: subtle pill — strip Tailwind's added quotes
|
|
490
|
+
'code::before': { content: 'none' },
|
|
491
|
+
'code::after': { content: 'none' },
|
|
492
|
+
code: {
|
|
493
|
+
backgroundColor: 'var(--surface-gray-2)',
|
|
494
|
+
borderRadius: '4px',
|
|
495
|
+
paddingTop: '1px',
|
|
496
|
+
paddingBottom: '1px',
|
|
497
|
+
paddingInlineStart: '5px',
|
|
498
|
+
paddingInlineEnd: '5px',
|
|
499
|
+
fontWeight: 420,
|
|
500
|
+
fontSize: em(12, 14),
|
|
501
|
+
},
|
|
502
|
+
// code inside pre should not get the pill styles
|
|
503
|
+
'pre code': {
|
|
504
|
+
backgroundColor: 'transparent',
|
|
505
|
+
borderRadius: '0',
|
|
506
|
+
padding: '0',
|
|
507
|
+
fontWeight: 'inherit',
|
|
508
|
+
fontSize: 'inherit',
|
|
509
|
+
},
|
|
510
|
+
|
|
511
|
+
// blockquote: left border, receded color, no italic, no quote marks
|
|
512
|
+
blockquote: {
|
|
513
|
+
'border-inline-start-width': '2px',
|
|
514
|
+
borderInlineStartColor: 'var(--ink-gray-3)',
|
|
515
|
+
borderInlineStartStyle: 'solid',
|
|
516
|
+
marginTop: '16px',
|
|
517
|
+
marginBottom: '16px',
|
|
518
|
+
paddingInlineStart: '1em',
|
|
519
|
+
fontStyle: 'normal',
|
|
520
|
+
color: 'var(--ink-gray-6)',
|
|
521
|
+
quotes: 'none',
|
|
522
|
+
},
|
|
523
|
+
'blockquote p:first-of-type::before': { content: 'none' },
|
|
524
|
+
'blockquote p:last-of-type::after': { content: 'none' },
|
|
525
|
+
'blockquote p': {
|
|
526
|
+
marginTop: '0',
|
|
527
|
+
marginBottom: '0',
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
// paragraphs: zero margin — user controls spacing with empty paragraphs
|
|
531
|
+
p: {
|
|
532
|
+
marginTop: '0',
|
|
533
|
+
marginBottom: '0',
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
// headings: marginTop creates section break (32/24px),
|
|
537
|
+
// marginBottom keeps heading close to its content (8px, proximity)
|
|
538
|
+
// h1/h2: full weight + darkest; h3-h5: softer weight + stepped-back color
|
|
539
|
+
h1: {
|
|
540
|
+
fontSize: em(20, 14),
|
|
541
|
+
marginTop: '32px',
|
|
542
|
+
marginBottom: '8px',
|
|
543
|
+
lineHeight: '1.3',
|
|
544
|
+
},
|
|
545
|
+
h2: {
|
|
546
|
+
fontSize: em(18, 14),
|
|
547
|
+
marginTop: '32px',
|
|
548
|
+
marginBottom: '8px',
|
|
549
|
+
lineHeight: '1.35',
|
|
550
|
+
},
|
|
551
|
+
h3: {
|
|
552
|
+
fontSize: em(16, 14),
|
|
553
|
+
marginTop: '24px',
|
|
554
|
+
marginBottom: '8px',
|
|
555
|
+
lineHeight: '1.4',
|
|
556
|
+
},
|
|
557
|
+
h4: {
|
|
558
|
+
fontSize: em(14, 14),
|
|
559
|
+
marginTop: '24px',
|
|
560
|
+
marginBottom: '8px',
|
|
561
|
+
lineHeight: '1.45',
|
|
562
|
+
},
|
|
563
|
+
h5: {
|
|
564
|
+
fontSize: em(13, 14),
|
|
565
|
+
marginTop: '24px',
|
|
566
|
+
marginBottom: '8px',
|
|
567
|
+
lineHeight: '1.45',
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
// element after heading gets no extra top margin
|
|
571
|
+
'h1 + *': { marginTop: '0' },
|
|
572
|
+
'h2 + *': { marginTop: '0' },
|
|
573
|
+
'h3 + *': { marginTop: '0' },
|
|
574
|
+
'h4 + *': { marginTop: '0' },
|
|
575
|
+
'h5 + *': { marginTop: '0' },
|
|
576
|
+
|
|
577
|
+
// lists: small outer margin (4px), tight internal spacing
|
|
578
|
+
ul: {
|
|
579
|
+
marginTop: '4px',
|
|
580
|
+
marginBottom: '4px',
|
|
581
|
+
paddingInlineStart: '1.5em',
|
|
582
|
+
},
|
|
583
|
+
ol: {
|
|
584
|
+
marginTop: '4px',
|
|
585
|
+
marginBottom: '4px',
|
|
586
|
+
paddingInlineStart: '1.7em',
|
|
587
|
+
},
|
|
588
|
+
li: {
|
|
589
|
+
marginTop: '4px',
|
|
590
|
+
marginBottom: '4px',
|
|
591
|
+
},
|
|
592
|
+
'li p': {
|
|
593
|
+
marginTop: '4px',
|
|
594
|
+
marginBottom: '4px',
|
|
595
|
+
},
|
|
596
|
+
'ul ul, ul ol, ol ul, ol ol': {
|
|
597
|
+
marginTop: '4px',
|
|
598
|
+
marginBottom: '4px',
|
|
599
|
+
},
|
|
600
|
+
|
|
601
|
+
// code blocks: breathing room (16px)
|
|
602
|
+
pre: {
|
|
603
|
+
fontSize: em(12, 14),
|
|
604
|
+
lineHeight: '1.6',
|
|
605
|
+
marginTop: '16px',
|
|
606
|
+
marginBottom: '16px',
|
|
607
|
+
borderRadius: '0.375rem',
|
|
608
|
+
paddingTop: '0.75em',
|
|
609
|
+
paddingInlineEnd: '1em',
|
|
610
|
+
paddingBottom: '0.75em',
|
|
611
|
+
paddingInlineStart: '1em',
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
// tables: breathing room (16px)
|
|
615
|
+
table: {
|
|
616
|
+
fontSize: em(12, 14),
|
|
617
|
+
lineHeight: '1.5',
|
|
618
|
+
marginTop: '16px',
|
|
619
|
+
marginBottom: '16px',
|
|
620
|
+
},
|
|
621
|
+
|
|
622
|
+
// images, video, figures: breathing room (16px)
|
|
623
|
+
img: {
|
|
624
|
+
marginTop: '16px',
|
|
625
|
+
marginBottom: '16px',
|
|
626
|
+
},
|
|
627
|
+
picture: {
|
|
628
|
+
marginTop: '16px',
|
|
629
|
+
marginBottom: '16px',
|
|
630
|
+
},
|
|
631
|
+
'picture > img': {
|
|
632
|
+
marginTop: '0',
|
|
633
|
+
marginBottom: '0',
|
|
634
|
+
},
|
|
635
|
+
video: {
|
|
636
|
+
marginTop: '16px',
|
|
637
|
+
marginBottom: '16px',
|
|
638
|
+
},
|
|
639
|
+
figure: {
|
|
640
|
+
marginTop: '16px',
|
|
641
|
+
marginBottom: '16px',
|
|
642
|
+
},
|
|
643
|
+
'figure > *': {
|
|
644
|
+
marginTop: '0',
|
|
645
|
+
marginBottom: '0',
|
|
646
|
+
},
|
|
647
|
+
|
|
648
|
+
// hr: short centered line, not edge-to-edge
|
|
649
|
+
hr: {
|
|
650
|
+
marginTop: '24px',
|
|
651
|
+
marginBottom: '24px',
|
|
652
|
+
marginLeft: 'auto',
|
|
653
|
+
marginRight: 'auto',
|
|
654
|
+
width: '20%',
|
|
655
|
+
},
|
|
656
|
+
'hr + *': {
|
|
657
|
+
marginTop: '0',
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
// first/last child: no extra margin
|
|
662
|
+
'> :first-child': {
|
|
663
|
+
marginTop: '0',
|
|
664
|
+
},
|
|
665
|
+
'> :last-child': {
|
|
666
|
+
marginBottom: '0',
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
],
|
|
670
|
+
},
|
|
671
|
+
// prose-p-spacing: restores paragraph margins for content authored before prose-v3.
|
|
672
|
+
// Apply alongside prose-v3 for pre-migration content:
|
|
673
|
+
// new content: "prose prose-v3"
|
|
674
|
+
// old content: "prose prose-v3 prose-p-spacing"
|
|
675
|
+
// Must be defined after v3 in this config so its rules come later in
|
|
676
|
+
// the generated CSS and override v3's zero paragraph margins.
|
|
677
|
+
'p-spacing': {
|
|
678
|
+
css: {
|
|
679
|
+
p: {
|
|
680
|
+
marginTop: '0.5rem',
|
|
681
|
+
marginBottom: '0.5rem',
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
}),
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
function em(pixels, base = 16) {
|
|
692
|
+
return `${pixels / base}em`
|
|
693
|
+
}
|