@yarcl/react 0.1.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 +53 -0
- package/dist/color.d.ts +12 -0
- package/dist/color.js +41 -0
- package/dist/css.d.ts +10 -0
- package/dist/css.js +175 -0
- package/dist/define.d.ts +383 -0
- package/dist/define.js +29 -0
- package/dist/plugin.d.ts +33 -0
- package/dist/plugin.js +80 -0
- package/package.json +66 -0
- package/src/apply.ts +46 -0
- package/src/classes.ts +23 -0
- package/src/color.ts +48 -0
- package/src/components/Accordion.tsx +274 -0
- package/src/components/Alert.tsx +85 -0
- package/src/components/Badge.tsx +79 -0
- package/src/components/Breadcrumb.tsx +239 -0
- package/src/components/Button.tsx +58 -0
- package/src/components/ButtonGroup.tsx +61 -0
- package/src/components/Card.tsx +50 -0
- package/src/components/Checkbox.tsx +49 -0
- package/src/components/Combobox.tsx +522 -0
- package/src/components/CommandPalette.tsx +452 -0
- package/src/components/DatePicker.tsx +432 -0
- package/src/components/Dialog.tsx +39 -0
- package/src/components/Divider.tsx +34 -0
- package/src/components/Drawer.tsx +31 -0
- package/src/components/Field.tsx +61 -0
- package/src/components/Heading.tsx +40 -0
- package/src/components/HoverCard.tsx +108 -0
- package/src/components/IconButton.tsx +59 -0
- package/src/components/Inline.tsx +42 -0
- package/src/components/Input.tsx +28 -0
- package/src/components/Label.tsx +58 -0
- package/src/components/Link.tsx +41 -0
- package/src/components/Menu.tsx +207 -0
- package/src/components/Modal.tsx +164 -0
- package/src/components/NumberInput.tsx +208 -0
- package/src/components/Pagination.tsx +227 -0
- package/src/components/Popover.tsx +140 -0
- package/src/components/Progress.tsx +122 -0
- package/src/components/Radio.tsx +59 -0
- package/src/components/RadioGroup.tsx +114 -0
- package/src/components/Select.tsx +197 -0
- package/src/components/Skeleton.tsx +95 -0
- package/src/components/Slider.tsx +315 -0
- package/src/components/Spinner.tsx +38 -0
- package/src/components/Stack.tsx +57 -0
- package/src/components/Switch.tsx +42 -0
- package/src/components/Table.tsx +99 -0
- package/src/components/Tabs.tsx +159 -0
- package/src/components/Text.tsx +67 -0
- package/src/components/Textarea.tsx +26 -0
- package/src/components/Toast.tsx +190 -0
- package/src/components/ToggleGroup.tsx +227 -0
- package/src/components/Tooltip.tsx +78 -0
- package/src/contrast.ts +82 -0
- package/src/css.ts +228 -0
- package/src/define.check.ts +259 -0
- package/src/define.ts +379 -0
- package/src/field-context.ts +31 -0
- package/src/floating.tsx +61 -0
- package/src/hooks.ts +19 -0
- package/src/index.ts +119 -0
- package/src/plugin.ts +94 -0
- package/src/reference/DesignReference.tsx +374 -0
- package/src/reference/index.ts +2 -0
- package/src/reference/reference.css +58 -0
- package/src/runtime.ts +38 -0
- package/src/styles.css +1971 -0
- package/src/themes/bloom.ts +72 -0
- package/src/themes/brutalist.ts +63 -0
- package/src/themes/compact.ts +63 -0
- package/src/themes/contract.ts +24 -0
- package/src/themes/editorial.ts +55 -0
- package/src/themes/index.ts +47 -0
- package/src/types.ts +69 -0
- package/src/yarcl.config.ts +104 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
autoUpdate,
|
|
3
|
+
FloatingArrow,
|
|
4
|
+
FloatingPortal,
|
|
5
|
+
useDismiss,
|
|
6
|
+
useFloating,
|
|
7
|
+
useFocus,
|
|
8
|
+
useHover,
|
|
9
|
+
useInteractions,
|
|
10
|
+
useRole,
|
|
11
|
+
type Placement,
|
|
12
|
+
} from '@floating-ui/react';
|
|
13
|
+
import { useRef, useState, type ReactElement, type ReactNode } from 'react';
|
|
14
|
+
import { floatingMiddleware, useTrigger } from '../floating';
|
|
15
|
+
import { useConfig } from '../runtime';
|
|
16
|
+
|
|
17
|
+
/** Props for {@link Tooltip}. */
|
|
18
|
+
export interface TooltipProps {
|
|
19
|
+
/** Short text describing the trigger. Keep it plain; tooltips can't contain interactive content. */
|
|
20
|
+
content: ReactNode;
|
|
21
|
+
/** A single focusable element, e.g. an {@link IconButton}. */
|
|
22
|
+
children: ReactElement;
|
|
23
|
+
/**
|
|
24
|
+
* Preferred side. Flips when there isn't room.
|
|
25
|
+
* @default 'top'
|
|
26
|
+
*/
|
|
27
|
+
placement?: Placement;
|
|
28
|
+
/**
|
|
29
|
+
* Delay before opening on hover, in ms. Opens immediately on keyboard focus.
|
|
30
|
+
* @default config.timing.tooltipDelay
|
|
31
|
+
*/
|
|
32
|
+
delay?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A short label shown on hover and keyboard focus.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <Tooltip content="Search">
|
|
41
|
+
* <IconButton aria-label="Search"><SearchIcon /></IconButton>
|
|
42
|
+
* </Tooltip>
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function Tooltip({ content, children, placement = 'top', delay }: TooltipProps) {
|
|
46
|
+
const config = useConfig();
|
|
47
|
+
const [open, setOpen] = useState(false);
|
|
48
|
+
const arrowRef = useRef<SVGSVGElement>(null);
|
|
49
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
50
|
+
open,
|
|
51
|
+
onOpenChange: setOpen,
|
|
52
|
+
placement,
|
|
53
|
+
transform: false,
|
|
54
|
+
whileElementsMounted: autoUpdate,
|
|
55
|
+
middleware: floatingMiddleware({ gap: 8, arrowRef }),
|
|
56
|
+
});
|
|
57
|
+
const { getReferenceProps, getFloatingProps } = useInteractions([
|
|
58
|
+
useHover(context, { move: false, delay: { open: delay ?? config.timing.tooltipDelay, close: 0 } }),
|
|
59
|
+
useFocus(context),
|
|
60
|
+
useDismiss(context),
|
|
61
|
+
useRole(context, { role: 'tooltip' }),
|
|
62
|
+
]);
|
|
63
|
+
const trigger = useTrigger(children, refs.setReference, getReferenceProps);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
{trigger}
|
|
68
|
+
{open && (
|
|
69
|
+
<FloatingPortal>
|
|
70
|
+
<div ref={refs.setFloating} style={floatingStyles} className="yarcl-floating yarcl-tooltip" {...getFloatingProps()}>
|
|
71
|
+
{content}
|
|
72
|
+
<FloatingArrow ref={arrowRef} context={context} className="yarcl-tooltip-arrow" width={10} height={5} />
|
|
73
|
+
</div>
|
|
74
|
+
</FloatingPortal>
|
|
75
|
+
)}
|
|
76
|
+
</>
|
|
77
|
+
);
|
|
78
|
+
}
|
package/src/contrast.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { contrast, parseHex, readableOn } from './color.ts';
|
|
2
|
+
import type { ColorPair, YarclShape } from './define.ts';
|
|
3
|
+
|
|
4
|
+
/** A color combination in a config that falls below the required contrast. */
|
|
5
|
+
export interface ContrastIssue {
|
|
6
|
+
/** The config path of the text color, e.g. `colors.warning` or `neutrals.muted`. */
|
|
7
|
+
path: string;
|
|
8
|
+
/** The color scheme the issue occurs in. */
|
|
9
|
+
scheme: 'light' | 'dark';
|
|
10
|
+
/** The contrast ratio, or `null` when it can't be computed (non-hex values). */
|
|
11
|
+
ratio: number | null;
|
|
12
|
+
/** A readable description of the problem. */
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const schemes = ['light', 'dark'] as const;
|
|
17
|
+
|
|
18
|
+
function asPair(value: string | ColorPair): ColorPair {
|
|
19
|
+
return typeof value === 'string' ? { light: value, dark: value } : value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checks a config for text that doesn't reach a contrast ratio (WCAG AA, 4.5:1, by default)
|
|
24
|
+
* in light or dark mode. Returns the problems instead of printing them, so you can run it in a
|
|
25
|
+
* test, a CI step or a script.
|
|
26
|
+
*
|
|
27
|
+
* Checks the text drawn on each semantic color (its computed or configured `on`), and the
|
|
28
|
+
* neutral `text` and `muted` colors on the `bg` and `surface` backgrounds. Colors drawn as text
|
|
29
|
+
* need no check: their readable shade is computed automatically.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { checkContrast } from '@yarcl/react/contrast';
|
|
34
|
+
* import config from './src/yarcl.config';
|
|
35
|
+
*
|
|
36
|
+
* test('design system is readable', () => {
|
|
37
|
+
* expect(checkContrast(config)).toEqual([]);
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function checkContrast(config: YarclShape, min = 4.5): ContrastIssue[] {
|
|
42
|
+
const issues: ContrastIssue[] = [];
|
|
43
|
+
|
|
44
|
+
for (const [key, token] of Object.entries(config.colors)) {
|
|
45
|
+
const on = token.on ? asPair(token.on) : null;
|
|
46
|
+
for (const scheme of schemes) {
|
|
47
|
+
const bg = parseHex(token[scheme]);
|
|
48
|
+
const path = `colors.${key}`;
|
|
49
|
+
if (!bg || (on && !parseHex(on[scheme]))) {
|
|
50
|
+
issues.push({ path, scheme, ratio: null, message: `${path}: can't check ${scheme} contrast of non-hex values; set \`on\` to a hex value` });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const fg = on ? parseHex(on[scheme])! : parseHex(readableOn(bg))!;
|
|
54
|
+
const ratio = contrast(bg, fg);
|
|
55
|
+
if (ratio < min) {
|
|
56
|
+
issues.push({ path, scheme, ratio, message: `${path}: ${scheme} text on this color is ${ratio.toFixed(2)}:1, below ${min}:1` });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { neutrals } = config;
|
|
62
|
+
for (const text of ['text', 'muted'] as const) {
|
|
63
|
+
for (const ground of ['bg', 'surface'] as const) {
|
|
64
|
+
for (const scheme of schemes) {
|
|
65
|
+
const fg = parseHex(neutrals[text][scheme]);
|
|
66
|
+
const bg = parseHex(neutrals[ground][scheme]);
|
|
67
|
+
if (!fg || !bg) continue;
|
|
68
|
+
const ratio = contrast(fg, bg);
|
|
69
|
+
if (ratio < min) {
|
|
70
|
+
issues.push({
|
|
71
|
+
path: `neutrals.${text}`,
|
|
72
|
+
scheme,
|
|
73
|
+
ratio,
|
|
74
|
+
message: `neutrals.${text} on neutrals.${ground}: ${scheme} contrast is ${ratio.toFixed(2)}:1, below ${min}:1`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return issues;
|
|
82
|
+
}
|
package/src/css.ts
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { contrast, mix, parseHex, readableOn, readableText, toHex } from './color.ts';
|
|
2
|
+
import type { ColorPair, ColorToken, FontFaceToken, VariantToken, YarclShape } from './define.ts';
|
|
3
|
+
|
|
4
|
+
const MIN_CONTRAST = 4.5;
|
|
5
|
+
|
|
6
|
+
function ident(key: string): string {
|
|
7
|
+
return key.replace(/[^a-zA-Z0-9_-]/g, (c) => `\\${c}`);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function pair({ light, dark }: ColorPair): string {
|
|
11
|
+
return light === dark ? light : `light-dark(${light}, ${dark})`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function foreground(name: string, token: ColorToken, warn: (message: string) => void): string {
|
|
15
|
+
const modes = ['light', 'dark'] as const;
|
|
16
|
+
const on = typeof token.on === 'string' ? { light: token.on, dark: token.on } : token.on;
|
|
17
|
+
|
|
18
|
+
const resolved = modes.map((mode) => {
|
|
19
|
+
const bg = parseHex(token[mode]);
|
|
20
|
+
if (on) {
|
|
21
|
+
const fg = parseHex(on[mode]);
|
|
22
|
+
if (bg && fg && contrast(bg, fg) < MIN_CONTRAST) {
|
|
23
|
+
warn(`colors.${name}: ${mode} foreground contrast ${contrast(bg, fg).toFixed(2)}:1 is below ${MIN_CONTRAST}:1`);
|
|
24
|
+
}
|
|
25
|
+
return on[mode];
|
|
26
|
+
}
|
|
27
|
+
if (!bg) {
|
|
28
|
+
warn(`colors.${name}: cannot compute a ${mode} foreground from "${token[mode]}"; use a hex value or set \`on\``);
|
|
29
|
+
return '#ffffff';
|
|
30
|
+
}
|
|
31
|
+
const fg = readableOn(bg);
|
|
32
|
+
const ratio = contrast(bg, parseHex(fg)!);
|
|
33
|
+
if (ratio < MIN_CONTRAST) {
|
|
34
|
+
warn(`colors.${name}: best ${mode} foreground contrast is ${ratio.toFixed(2)}:1, below ${MIN_CONTRAST}:1`);
|
|
35
|
+
}
|
|
36
|
+
return fg;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return pair({ light: resolved[0], dark: resolved[1] });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const tint = (percent: number) => `color-mix(in oklab, var(--yarcl-c) ${percent}%, transparent)`;
|
|
43
|
+
const shade = (percent: number) => `color-mix(in oklab, var(--yarcl-c), var(--yarcl-c-on) ${percent}%)`;
|
|
44
|
+
|
|
45
|
+
const backgrounds: Record<VariantToken['background'], [string, string, string]> = {
|
|
46
|
+
fill: ['var(--yarcl-c)', shade(12), shade(20)],
|
|
47
|
+
tint: [tint(14), tint(22), tint(30)],
|
|
48
|
+
none: ['transparent', tint(10), tint(18)],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const borders: Record<VariantToken['border'], string> = {
|
|
52
|
+
color: 'var(--yarcl-c)',
|
|
53
|
+
neutral: 'var(--yarcl-neutral-border)',
|
|
54
|
+
none: 'transparent',
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const texts: Record<VariantToken['text'], string> = {
|
|
58
|
+
on: 'var(--yarcl-c-on)',
|
|
59
|
+
color: 'var(--yarcl-c-text)',
|
|
60
|
+
neutral: 'var(--yarcl-neutral-text)',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const formats: Record<string, string> = { woff2: 'woff2', woff: 'woff', ttf: 'truetype', otf: 'opentype' };
|
|
64
|
+
|
|
65
|
+
function fontFace(face: FontFaceToken): string {
|
|
66
|
+
const sources = (Array.isArray(face.src) ? face.src : [face.src]).map((src) => {
|
|
67
|
+
if (src.startsWith('local(')) return src;
|
|
68
|
+
const format = formats[src.split(/[?#]/)[0].split('.').pop()?.toLowerCase() ?? ''];
|
|
69
|
+
return `url(${JSON.stringify(src)})${format ? ` format("${format}")` : ''}`;
|
|
70
|
+
});
|
|
71
|
+
return rule('@font-face', [
|
|
72
|
+
['font-family', JSON.stringify(face.family)],
|
|
73
|
+
['src', sources.join(', ')],
|
|
74
|
+
...(face.weight != null ? [['font-weight', face.weight] as [string, string | number]] : []),
|
|
75
|
+
...(face.style ? [['font-style', face.style] as [string, string]] : []),
|
|
76
|
+
['font-display', face.display ?? 'swap'],
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const TEXT_TINTS = [0, 0.14, 0.22];
|
|
81
|
+
|
|
82
|
+
function textShade(name: string, token: ColorToken, neutrals: YarclShape['neutrals']): string {
|
|
83
|
+
if (token.text) return pair(typeof token.text === 'string' ? { light: token.text, dark: token.text } : token.text);
|
|
84
|
+
const modes = ['light', 'dark'] as const;
|
|
85
|
+
const resolved = modes.map((mode) => {
|
|
86
|
+
const color = parseHex(token[mode]);
|
|
87
|
+
const text = parseHex(neutrals.text[mode]);
|
|
88
|
+
const grounds = [parseHex(neutrals.bg[mode]), parseHex(neutrals.surface[mode])];
|
|
89
|
+
if (!color || !text || grounds.some((g) => !g)) return token[mode];
|
|
90
|
+
const backgrounds = grounds.flatMap((ground) => TEXT_TINTS.map((alpha) => mix(ground!, color, alpha)));
|
|
91
|
+
return toHex(readableText(color, backgrounds, text, MIN_CONTRAST));
|
|
92
|
+
});
|
|
93
|
+
return pair({ light: resolved[0], dark: resolved[1] });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function rule(selector: string, declarations: [string, string | number][]): string {
|
|
97
|
+
return `${selector} {\n${declarations.map(([p, v]) => ` ${p}: ${v};`).join('\n')}\n}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generates the stylesheet for a config: CSS variables on `:root`, one class per key and
|
|
102
|
+
* `@font-face` rules. The Vite plugin calls this at build time; call it yourself to preview
|
|
103
|
+
* or switch themes at runtime (inject the result into a `<style>` element).
|
|
104
|
+
*
|
|
105
|
+
* @param config A config from `defineConfig`, e.g. one of `@yarcl/react/themes`.
|
|
106
|
+
* @param warn Receives contrast warnings.
|
|
107
|
+
*/
|
|
108
|
+
export function generateCss(config: YarclShape, warn: (message: string) => void = () => {}): string {
|
|
109
|
+
const root: [string, string | number][] = [['color-scheme', 'light dark']];
|
|
110
|
+
const rules: string[] = [];
|
|
111
|
+
|
|
112
|
+
for (const [key, token] of Object.entries(config.colors)) {
|
|
113
|
+
const k = ident(key);
|
|
114
|
+
root.push(
|
|
115
|
+
[`--yarcl-color-${k}`, pair(token)],
|
|
116
|
+
[`--yarcl-color-${k}-on`, foreground(key, token, warn)],
|
|
117
|
+
[`--yarcl-color-${k}-text`, textShade(key, token, config.neutrals)],
|
|
118
|
+
);
|
|
119
|
+
rules.push(
|
|
120
|
+
rule(`.yarcl-color-${k}`, [
|
|
121
|
+
['--yarcl-c', `var(--yarcl-color-${k})`],
|
|
122
|
+
['--yarcl-c-on', `var(--yarcl-color-${k}-on)`],
|
|
123
|
+
['--yarcl-c-text', `var(--yarcl-color-${k}-text)`],
|
|
124
|
+
]),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const [key, value] of Object.entries(config.neutrals)) root.push([`--yarcl-neutral-${ident(key)}`, pair(value)]);
|
|
129
|
+
|
|
130
|
+
for (const [key, size] of Object.entries(config.sizes)) {
|
|
131
|
+
const k = ident(key);
|
|
132
|
+
root.push(
|
|
133
|
+
[`--yarcl-size-${k}-height`, size.height],
|
|
134
|
+
[`--yarcl-size-${k}-padding-x`, size.paddingX],
|
|
135
|
+
[`--yarcl-size-${k}-font-size`, size.fontSize],
|
|
136
|
+
[`--yarcl-size-${k}-icon-size`, size.iconSize],
|
|
137
|
+
);
|
|
138
|
+
rules.push(
|
|
139
|
+
rule(`.yarcl-size-${k}`, [
|
|
140
|
+
['--yarcl-h', `var(--yarcl-size-${k}-height)`],
|
|
141
|
+
['--yarcl-px', `var(--yarcl-size-${k}-padding-x)`],
|
|
142
|
+
['--yarcl-fs', `var(--yarcl-size-${k}-font-size)`],
|
|
143
|
+
['--yarcl-icon', `var(--yarcl-size-${k}-icon-size)`],
|
|
144
|
+
]),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const [key, value] of Object.entries(config.radii)) {
|
|
149
|
+
const k = ident(key);
|
|
150
|
+
root.push([`--yarcl-radius-${k}`, value]);
|
|
151
|
+
rules.push(rule(`.yarcl-radius-${k}`, [['--yarcl-r', `var(--yarcl-radius-${k})`]]));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
for (const [key, variant] of Object.entries(config.variants)) {
|
|
155
|
+
const [bg, hover, active] = backgrounds[variant.background];
|
|
156
|
+
rules.push(
|
|
157
|
+
rule(`.yarcl-variant-${ident(key)}`, [
|
|
158
|
+
['--yarcl-v-bg', bg],
|
|
159
|
+
['--yarcl-v-bg-hover', hover],
|
|
160
|
+
['--yarcl-v-bg-active', active],
|
|
161
|
+
['--yarcl-v-border', borders[variant.border]],
|
|
162
|
+
['--yarcl-v-fg', texts[variant.text]],
|
|
163
|
+
]),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const [key, value] of Object.entries(config.spacing)) {
|
|
168
|
+
const k = ident(key);
|
|
169
|
+
root.push([`--yarcl-space-${k}`, value]);
|
|
170
|
+
rules.push(
|
|
171
|
+
rule(`.yarcl-gap-${k}`, [['gap', `var(--yarcl-space-${k})`]]),
|
|
172
|
+
rule(`.yarcl-padding-${k}`, [['padding', `var(--yarcl-space-${k})`]]),
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const [key, value] of Object.entries(config.shadows)) {
|
|
177
|
+
const k = ident(key);
|
|
178
|
+
root.push([`--yarcl-shadow-${k}`, value]);
|
|
179
|
+
rules.push(rule(`.yarcl-shadow-${k}`, [['box-shadow', `var(--yarcl-shadow-${k})`]]));
|
|
180
|
+
}
|
|
181
|
+
for (const [key, value] of Object.entries(config.modalSizes)) {
|
|
182
|
+
const k = ident(key);
|
|
183
|
+
root.push([`--yarcl-modal-${k}`, value]);
|
|
184
|
+
rules.push(rule(`.yarcl-modal-size-${k}`, [['--yarcl-modal-width', `var(--yarcl-modal-${k})`]]));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
for (const [key, density] of Object.entries(config.density)) {
|
|
188
|
+
rules.push(
|
|
189
|
+
rule(`.yarcl-density-${ident(key)}`, [
|
|
190
|
+
['--yarcl-cell-px', density.paddingX],
|
|
191
|
+
['--yarcl-cell-py', density.paddingY],
|
|
192
|
+
['--yarcl-cell-fs', density.fontSize],
|
|
193
|
+
]),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for (const [key, value] of Object.entries(config.typography.families)) root.push([`--yarcl-font-${ident(key)}`, value]);
|
|
198
|
+
|
|
199
|
+
for (const [key, style] of Object.entries(config.typography.styles)) {
|
|
200
|
+
rules.push(
|
|
201
|
+
rule(`.yarcl-type-${ident(key)}`, [
|
|
202
|
+
['font-family', `var(--yarcl-font-${ident(style.family)})`],
|
|
203
|
+
['font-size', style.size],
|
|
204
|
+
['font-weight', style.weight],
|
|
205
|
+
['line-height', style.lineHeight],
|
|
206
|
+
...(style.letterSpacing ? [['letter-spacing', style.letterSpacing] as [string, string]] : []),
|
|
207
|
+
]),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
for (const [key, value] of Object.entries(config.zIndex)) root.push([`--yarcl-z-${ident(key)}`, value]);
|
|
212
|
+
for (const [key, value] of Object.entries(config.motion)) root.push([`--yarcl-motion-${ident(key)}`, value]);
|
|
213
|
+
for (const [key, value] of Object.entries(config.borders)) root.push([`--yarcl-border-${ident(key)}`, value]);
|
|
214
|
+
|
|
215
|
+
root.push(
|
|
216
|
+
['--yarcl-focus-width', config.focusRing.width],
|
|
217
|
+
['--yarcl-focus-offset', config.focusRing.offset],
|
|
218
|
+
['--yarcl-focus-style', config.focusRing.style ?? 'solid'],
|
|
219
|
+
['--yarcl-focus-color', `var(--yarcl-color-${ident(config.focusRing.color)})`],
|
|
220
|
+
['--yarcl-error', `var(--yarcl-color-${ident(config.defaults.errorColor)})`],
|
|
221
|
+
['--yarcl-floating-shadow', `var(--yarcl-shadow-${ident(config.defaults.floatingShadow)})`],
|
|
222
|
+
['--yarcl-padding', `var(--yarcl-space-${ident(config.defaults.padding)})`],
|
|
223
|
+
['--yarcl-gap', `var(--yarcl-space-${ident(config.defaults.gap)})`],
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const faces = (config.typography.fontFaces ?? []).map(fontFace);
|
|
227
|
+
return [...faces, rule(':root', root), ...rules].join('\n\n') + '\n';
|
|
228
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { defineConfig } from './define';
|
|
2
|
+
import defaults from './yarcl.config';
|
|
3
|
+
|
|
4
|
+
defineConfig({ ...defaults, colors: { ...defaults.colors, brand: { light: '#000', dark: '#fff' } } });
|
|
5
|
+
|
|
6
|
+
defineConfig({
|
|
7
|
+
...defaults,
|
|
8
|
+
// @ts-expect-error
|
|
9
|
+
colors: { primary: { light: '#2d4bb8' } },
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
defineConfig({
|
|
13
|
+
...defaults,
|
|
14
|
+
// @ts-expect-error
|
|
15
|
+
defaults: { ...defaults.defaults, size: 'xxl' },
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
defineConfig({
|
|
19
|
+
...defaults,
|
|
20
|
+
// @ts-expect-error
|
|
21
|
+
focusRing: { ...defaults.focusRing, color: 'nope' },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
defineConfig({
|
|
25
|
+
...defaults,
|
|
26
|
+
typography: {
|
|
27
|
+
...defaults.typography,
|
|
28
|
+
// @ts-expect-error
|
|
29
|
+
styles: { body: { family: 'serif', size: '1rem', weight: 400, lineHeight: 1.5 } },
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
defineConfig({
|
|
34
|
+
...defaults,
|
|
35
|
+
// @ts-expect-error
|
|
36
|
+
radii: { ...defaults.radii, 'extra round': '2rem' },
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
defineConfig({
|
|
40
|
+
...defaults,
|
|
41
|
+
zIndex: { ...defaults.zIndex, banner: 900 },
|
|
42
|
+
neutrals: { ...defaults.neutrals, raised: { light: '#fff', dark: '#222' } },
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
defineConfig({
|
|
46
|
+
...defaults,
|
|
47
|
+
// @ts-expect-error
|
|
48
|
+
zIndex: { dropdown: 1000, tooltip: 1100, dialog: 1200 },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
defineConfig({
|
|
52
|
+
...defaults,
|
|
53
|
+
variants: { ...defaults.variants, link: { background: 'none', border: 'none', text: 'color' } },
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
defineConfig({
|
|
57
|
+
...defaults,
|
|
58
|
+
// @ts-expect-error
|
|
59
|
+
variants: { solid: { background: 'gradient', border: 'color', text: 'on' } },
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
defineConfig({
|
|
63
|
+
...defaults,
|
|
64
|
+
// @ts-expect-error
|
|
65
|
+
defaults: { ...defaults.defaults, errorColor: 'red' },
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
defineConfig({
|
|
69
|
+
...defaults,
|
|
70
|
+
// @ts-expect-error
|
|
71
|
+
defaults: { ...defaults.defaults, textStyle: 'huge' },
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
defineConfig({
|
|
75
|
+
...defaults,
|
|
76
|
+
// @ts-expect-error
|
|
77
|
+
defaults: { ...defaults.defaults, gap: 'tight' },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
defineConfig({
|
|
81
|
+
...defaults,
|
|
82
|
+
// @ts-expect-error
|
|
83
|
+
defaults: { ...defaults.defaults, softVariant: 'subtle' },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
defineConfig({
|
|
87
|
+
...defaults,
|
|
88
|
+
typography: {
|
|
89
|
+
...defaults.typography,
|
|
90
|
+
fontFaces: [{ family: 'Inter', src: ['/fonts/inter.woff2', 'local(Inter)'], weight: '100 900' }],
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
defineConfig({
|
|
95
|
+
...defaults,
|
|
96
|
+
typography: {
|
|
97
|
+
...defaults.typography,
|
|
98
|
+
// @ts-expect-error
|
|
99
|
+
headings: { ...defaults.typography.headings, h3: 'huge' },
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
defineConfig({
|
|
104
|
+
...defaults,
|
|
105
|
+
typography: {
|
|
106
|
+
...defaults.typography,
|
|
107
|
+
// @ts-expect-error
|
|
108
|
+
headings: { h1: 'display', h2: 'heading' },
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
defineConfig({
|
|
113
|
+
...defaults,
|
|
114
|
+
// @ts-expect-error
|
|
115
|
+
defaults: { ...defaults.defaults, labelStyle: 'tiny' },
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
defineConfig({
|
|
119
|
+
...defaults,
|
|
120
|
+
components: {
|
|
121
|
+
Button: { radius: 'square', variant: 'outline' },
|
|
122
|
+
IconButton: { radius: 'size' },
|
|
123
|
+
Card: { padding: 'xl', shadow: 'lg' },
|
|
124
|
+
Stack: { gap: 'sm' },
|
|
125
|
+
Pagination: { size: 'sm', variant: 'outline', selectedVariant: 'solid' },
|
|
126
|
+
Breadcrumb: { textStyle: 'caption', color: 'neutral' },
|
|
127
|
+
Accordion: { size: 'sm', radius: 'size', color: 'success' },
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
defineConfig({
|
|
132
|
+
...defaults,
|
|
133
|
+
components: {
|
|
134
|
+
// @ts-expect-error
|
|
135
|
+
Pagination: { selectedVariant: 'filled' },
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
defineConfig({
|
|
140
|
+
...defaults,
|
|
141
|
+
components: {
|
|
142
|
+
// @ts-expect-error
|
|
143
|
+
Breadcrumb: { size: 'sm' },
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
defineConfig({
|
|
148
|
+
...defaults,
|
|
149
|
+
components: {
|
|
150
|
+
// @ts-expect-error
|
|
151
|
+
Breadcrumb: { textStyle: 'huge' },
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
defineConfig({
|
|
156
|
+
...defaults,
|
|
157
|
+
components: {
|
|
158
|
+
// @ts-expect-error
|
|
159
|
+
Accordion: { variant: 'solid' },
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
defineConfig({
|
|
164
|
+
...defaults,
|
|
165
|
+
components: {
|
|
166
|
+
// @ts-expect-error
|
|
167
|
+
Buton: { radius: 'square' },
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
defineConfig({
|
|
172
|
+
...defaults,
|
|
173
|
+
components: {
|
|
174
|
+
// @ts-expect-error
|
|
175
|
+
Card: { variant: 'solid' },
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
defineConfig({
|
|
180
|
+
...defaults,
|
|
181
|
+
components: {
|
|
182
|
+
// @ts-expect-error
|
|
183
|
+
Button: { radius: 'pill' },
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
defineConfig({
|
|
188
|
+
...defaults,
|
|
189
|
+
// @ts-expect-error
|
|
190
|
+
radii: { ...defaults.radii, size: '1rem' },
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
defineConfig({ ...defaults, components: { CommandPalette: { size: 'lg', radius: 'size', color: 'danger' } } });
|
|
194
|
+
|
|
195
|
+
defineConfig({
|
|
196
|
+
...defaults,
|
|
197
|
+
components: {
|
|
198
|
+
// @ts-expect-error
|
|
199
|
+
CommandPalette: { variant: 'solid' },
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
defineConfig({ ...defaults, defaults: { ...defaults.defaults, radius: 'square' } });
|
|
204
|
+
|
|
205
|
+
defineConfig({
|
|
206
|
+
...defaults,
|
|
207
|
+
modalSizes: { ...defaults.modalSizes, huge: '80rem' },
|
|
208
|
+
components: { Dialog: { size: 'huge' }, Drawer: { size: 'sm' } },
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
defineConfig({
|
|
212
|
+
...defaults,
|
|
213
|
+
components: {
|
|
214
|
+
// @ts-expect-error
|
|
215
|
+
Dialog: { size: 'giant' },
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
defineConfig({ ...defaults, components: { Label: { textStyle: 'caption', color: 'neutral' } } });
|
|
220
|
+
|
|
221
|
+
defineConfig({
|
|
222
|
+
...defaults,
|
|
223
|
+
components: {
|
|
224
|
+
// @ts-expect-error
|
|
225
|
+
Label: { textStyle: 'tiny' },
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
defineConfig({
|
|
230
|
+
...defaults,
|
|
231
|
+
components: {
|
|
232
|
+
// @ts-expect-error
|
|
233
|
+
Label: { size: 'sm' },
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
defineConfig({
|
|
238
|
+
...defaults,
|
|
239
|
+
// @ts-expect-error
|
|
240
|
+
defaults: { ...defaults.defaults, modalSize: 'giant' },
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
defineConfig({
|
|
244
|
+
...defaults,
|
|
245
|
+
colors: { ...defaults.colors, primary: { light: '#2d4bb8', dark: '#8aa2ff', text: { light: '#1e3480', dark: '#c3cfff' } } },
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
defineConfig({
|
|
249
|
+
...defaults,
|
|
250
|
+
components: { Progress: { size: 'lg', color: 'success', radius: 'rounded' } },
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
defineConfig({
|
|
254
|
+
...defaults,
|
|
255
|
+
components: {
|
|
256
|
+
// @ts-expect-error
|
|
257
|
+
Progress: { variant: 'solid' },
|
|
258
|
+
},
|
|
259
|
+
});
|