@stonedogcode/style 0.9.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 +201 -0
- package/NOTICE +18 -0
- package/README.md +699 -0
- package/package.json +95 -0
- package/src/components/DictationControls.tsx +141 -0
- package/src/components/DictationPrompt.tsx +78 -0
- package/src/components/StyledBox.tsx +174 -0
- package/src/components/StyledButton.tsx +144 -0
- package/src/components/StyledCollapsible.tsx +127 -0
- package/src/components/StyledDefinitionList.tsx +134 -0
- package/src/components/StyledFieldset.tsx +157 -0
- package/src/components/StyledFlex.tsx +13 -0
- package/src/components/StyledFooter.tsx +399 -0
- package/src/components/StyledFormLabel.tsx +141 -0
- package/src/components/StyledGrid.tsx +109 -0
- package/src/components/StyledGridItem.tsx +19 -0
- package/src/components/StyledHStack.tsx +145 -0
- package/src/components/StyledHeading.tsx +79 -0
- package/src/components/StyledHrRule.tsx +33 -0
- package/src/components/StyledIcon.tsx +172 -0
- package/src/components/StyledIconButton.tsx +135 -0
- package/src/components/StyledInputBool.tsx +81 -0
- package/src/components/StyledInputRadio.tsx +141 -0
- package/src/components/StyledInputSelect.tsx +115 -0
- package/src/components/StyledInputSlider.tsx +83 -0
- package/src/components/StyledInputText.tsx +146 -0
- package/src/components/StyledInputTextArea.tsx +119 -0
- package/src/components/StyledInputToggle.tsx +224 -0
- package/src/components/StyledList.tsx +188 -0
- package/src/components/StyledScrollbar.tsx +53 -0
- package/src/components/StyledSearch.tsx +78 -0
- package/src/components/StyledSeparator.tsx +38 -0
- package/src/components/StyledSidebar.tsx +555 -0
- package/src/components/StyledSimpleGrid.tsx +99 -0
- package/src/components/StyledSparkLine.tsx +119 -0
- package/src/components/StyledSpinner.tsx +91 -0
- package/src/components/StyledStack.tsx +62 -0
- package/src/components/StyledText.tsx +99 -0
- package/src/components/StyledTooltip.tsx +398 -0
- package/src/components/StyledVStack.tsx +143 -0
- package/src/components/TitleLogo.tsx +223 -0
- package/src/components/create-icon.tsx +66 -0
- package/src/components/create-intent-button.tsx +134 -0
- package/src/components/dictation.ts +71 -0
- package/src/components/intent-buttons.ts +154 -0
- package/src/config/can-hover.ts +75 -0
- package/src/config/density.ts +138 -0
- package/src/config/font-size.ts +113 -0
- package/src/config/intent-icons.tsx +116 -0
- package/src/config/logger.ts +60 -0
- package/src/config/style-config.tsx +263 -0
- package/src/config/types.ts +137 -0
- package/src/index.ts +259 -0
- package/src/preset/index.ts +243 -0
- package/src/preset/recipes/arrows.ts +29 -0
- package/src/preset/recipes/box.ts +122 -0
- package/src/preset/recipes/button.ts +161 -0
- package/src/preset/recipes/dl-list.ts +109 -0
- package/src/preset/recipes/drawer.ts +125 -0
- package/src/preset/recipes/form.ts +95 -0
- package/src/preset/recipes/icon-button.ts +161 -0
- package/src/preset/recipes/icon.ts +34 -0
- package/src/preset/recipes/input-bool.ts +184 -0
- package/src/preset/recipes/input-dropdown.ts +93 -0
- package/src/preset/recipes/input-radio.ts +158 -0
- package/src/preset/recipes/input-surface.ts +152 -0
- package/src/preset/recipes/input-text.ts +17 -0
- package/src/preset/recipes/list.ts +196 -0
- package/src/preset/recipes/menu.ts +28 -0
- package/src/preset/recipes/separator.ts +89 -0
- package/src/preset/recipes/stack.ts +89 -0
- package/src/preset/recipes/striped.ts +34 -0
- package/src/preset/recipes/text.ts +41 -0
- package/src/preset/recipes/tooltip.ts +77 -0
- package/src/preset/semantic-variables.ts +283 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { defineRecipe } from '@pandacss/dev'
|
|
2
|
+
|
|
3
|
+
export const stackRecipe = defineRecipe({
|
|
4
|
+
className: 'stack',
|
|
5
|
+
base: {
|
|
6
|
+
display: 'flex',
|
|
7
|
+
alignItems: 'center',
|
|
8
|
+
},
|
|
9
|
+
variants: {
|
|
10
|
+
direction: {
|
|
11
|
+
row: {
|
|
12
|
+
flexDirection: 'row',
|
|
13
|
+
},
|
|
14
|
+
column: {
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
justify: {
|
|
19
|
+
start: {
|
|
20
|
+
justifyContent: 'flex-start',
|
|
21
|
+
},
|
|
22
|
+
center: {
|
|
23
|
+
justifyContent: 'center',
|
|
24
|
+
},
|
|
25
|
+
end: {
|
|
26
|
+
justifyContent: 'flex-end',
|
|
27
|
+
},
|
|
28
|
+
between: {
|
|
29
|
+
justifyContent: 'space-between',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
gap: {
|
|
33
|
+
'0': { gap: '0' },
|
|
34
|
+
'1': { gap: '1' },
|
|
35
|
+
'2': { gap: '2' },
|
|
36
|
+
'3': { gap: '3' },
|
|
37
|
+
'4': { gap: '4' },
|
|
38
|
+
'5': { gap: '5' },
|
|
39
|
+
'6': { gap: '6' },
|
|
40
|
+
},
|
|
41
|
+
variant: {
|
|
42
|
+
solid: {
|
|
43
|
+
bg: "textPrimary",
|
|
44
|
+
color: "textPrimary",
|
|
45
|
+
borderColor: "borderBgPrimary",
|
|
46
|
+
},
|
|
47
|
+
outline: {
|
|
48
|
+
borderColor: "borderBgSecondary",
|
|
49
|
+
color: "textPrimary",
|
|
50
|
+
_hover: {
|
|
51
|
+
bg: "textPrimary",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
aurora: {
|
|
55
|
+
// Quoted token names are CSS strings, so this gradient was invalid
|
|
56
|
+
// and never painted (NEH-301). `{colors.X}` substitutes.
|
|
57
|
+
backgroundImage: `linear-gradient(to right, {colors.boxBgAccent}, {colors.boxBgSecondary})`,
|
|
58
|
+
color: "textPrimary",
|
|
59
|
+
borderColor: "transparent",
|
|
60
|
+
},
|
|
61
|
+
glass: {
|
|
62
|
+
backdropFilter: "blur(10px)",
|
|
63
|
+
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
64
|
+
border: "1px solid rgba(255, 255, 255, 0.2)",
|
|
65
|
+
color: "textPrimary",
|
|
66
|
+
},
|
|
67
|
+
matte: {
|
|
68
|
+
// `secondary` was undefined vocabulary — never painted (NEH-301).
|
|
69
|
+
bg: "boxBgSecondary",
|
|
70
|
+
color: "textSecondary",
|
|
71
|
+
borderColor: "borderBgPrimary",
|
|
72
|
+
},
|
|
73
|
+
ghost: {
|
|
74
|
+
bg: "boxBgSecondary",
|
|
75
|
+
border: "none",
|
|
76
|
+
},
|
|
77
|
+
none: {
|
|
78
|
+
border: "none",
|
|
79
|
+
backgroundColor: "inherit",
|
|
80
|
+
},
|
|
81
|
+
unstyled: {
|
|
82
|
+
border: "none",
|
|
83
|
+
p: "0",
|
|
84
|
+
m: "0",
|
|
85
|
+
backgroundColor: "inherit",
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineRecipe } from "@pandacss/dev";
|
|
2
|
+
|
|
3
|
+
export const stripedRecipe = defineRecipe({
|
|
4
|
+
className: "striped",
|
|
5
|
+
description: "A recipe for applying striped styles to a list of items",
|
|
6
|
+
base: {
|
|
7
|
+
"& > *:nth-child(odd)": {
|
|
8
|
+
background: "boxBgSecondary",
|
|
9
|
+
height: "100%",
|
|
10
|
+
width: "100%",
|
|
11
|
+
},
|
|
12
|
+
"& > *:nth-child(even)": {
|
|
13
|
+
background: "boxBgSecondary",
|
|
14
|
+
color: "textAccent",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
variants: {
|
|
18
|
+
// Optional: Add a variant if you sometimes want to stripe odd rows instead
|
|
19
|
+
stripe: {
|
|
20
|
+
odd: {
|
|
21
|
+
"& > *:nth-child(even)": {
|
|
22
|
+
background: "transparent",
|
|
23
|
+
},
|
|
24
|
+
"& > *:nth-child(odd)": {
|
|
25
|
+
background: "boxBgSecondary",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
even: {}, // This is the default behavior from 'base'
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
stripe: "even",
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { defineRecipe } from "@pandacss/dev";
|
|
2
|
+
|
|
3
|
+
export const textRecipe = defineRecipe({
|
|
4
|
+
className: "text",
|
|
5
|
+
description: "The styles for the Text component",
|
|
6
|
+
base: {
|
|
7
|
+
color: "textPrimary",
|
|
8
|
+
// The theme's body face (NEH-289). StyledHeading renders through this
|
|
9
|
+
// recipe too and asks for the heading face at its call site instead.
|
|
10
|
+
fontFamily: "body",
|
|
11
|
+
},
|
|
12
|
+
variants: {
|
|
13
|
+
variant: {
|
|
14
|
+
unstyled: {
|
|
15
|
+
color: "inherit",
|
|
16
|
+
},
|
|
17
|
+
pop: {
|
|
18
|
+
color: "textPop",
|
|
19
|
+
},
|
|
20
|
+
warning: {
|
|
21
|
+
bg: "textPop",
|
|
22
|
+
py: "6",
|
|
23
|
+
px: {
|
|
24
|
+
base: "3",
|
|
25
|
+
md: "6",
|
|
26
|
+
},
|
|
27
|
+
color: "textWarning",
|
|
28
|
+
fontWeight: "bold",
|
|
29
|
+
},
|
|
30
|
+
error: {
|
|
31
|
+
bg: "textPop",
|
|
32
|
+
py: "2",
|
|
33
|
+
px: {
|
|
34
|
+
base: "1",
|
|
35
|
+
md: "3",
|
|
36
|
+
},
|
|
37
|
+
color: "textError",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { defineRecipe } from "@pandacss/dev";
|
|
2
|
+
|
|
3
|
+
export const tooltipRecipe = defineRecipe({
|
|
4
|
+
className: "tooltip",
|
|
5
|
+
base: {
|
|
6
|
+
position: "absolute",
|
|
7
|
+
zIndex: 9999,
|
|
8
|
+
pointerEvents: "none",
|
|
9
|
+
color: "buttonTextSecondary",
|
|
10
|
+
padding: "2px",
|
|
11
|
+
borderRadius: "md",
|
|
12
|
+
fontSize: "var(--font-sizes-lg, 1rem)",
|
|
13
|
+
boxShadow: "lg",
|
|
14
|
+
whiteSpace: "pre-line",
|
|
15
|
+
maxWidth: {
|
|
16
|
+
base: "340px",
|
|
17
|
+
md: "550px",
|
|
18
|
+
},
|
|
19
|
+
left: "50%",
|
|
20
|
+
top: "100%",
|
|
21
|
+
transformOrigin: "top center",
|
|
22
|
+
bg: "boxBgPrimary"
|
|
23
|
+
},
|
|
24
|
+
variants: {
|
|
25
|
+
variant: {
|
|
26
|
+
solid: {
|
|
27
|
+
bg: "boxBgPrimary",
|
|
28
|
+
color: "buttonTextSecondary",
|
|
29
|
+
borderColor: "borderBgPrimary",
|
|
30
|
+
},
|
|
31
|
+
outline: {
|
|
32
|
+
bg: "boxBgPrimary",
|
|
33
|
+
color: "textPrimary",
|
|
34
|
+
borderColor: "borderBgPrimary",
|
|
35
|
+
border: "1px solid",
|
|
36
|
+
},
|
|
37
|
+
aurora: {
|
|
38
|
+
bg: "boxBgPrimary",
|
|
39
|
+
color: "textSecondary",
|
|
40
|
+
borderColor: "borderBgSecondary",
|
|
41
|
+
border: "1px solid",
|
|
42
|
+
},
|
|
43
|
+
glass: {
|
|
44
|
+
bg: "boxBgPrimary",
|
|
45
|
+
color: "buttonTextPrimary",
|
|
46
|
+
border: "1px solid",
|
|
47
|
+
borderColor: "borderBgPrimary",
|
|
48
|
+
},
|
|
49
|
+
matte: {
|
|
50
|
+
bg: "boxBgPrimary",
|
|
51
|
+
color: "textSecondary",
|
|
52
|
+
borderColor: "borderBgSecondary",
|
|
53
|
+
border: "1px solid",
|
|
54
|
+
},
|
|
55
|
+
ghost: {
|
|
56
|
+
bg: "boxBgPrimary",
|
|
57
|
+
color: "buttonTextSecondary",
|
|
58
|
+
},
|
|
59
|
+
link: {
|
|
60
|
+
bg: "boxBgPrimary",
|
|
61
|
+
color: "textPrimary",
|
|
62
|
+
border: "1px solid",
|
|
63
|
+
borderColor: "borderBgPrimary",
|
|
64
|
+
},
|
|
65
|
+
none: {
|
|
66
|
+
bg: "boxBgPrimary",
|
|
67
|
+
color: "textMain",
|
|
68
|
+
border: "1px solid",
|
|
69
|
+
borderColor: "borderBgPrimary",
|
|
70
|
+
},
|
|
71
|
+
unstyled: {
|
|
72
|
+
bg: "boxBgPrimary",
|
|
73
|
+
color: "textMain",
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The token contract.
|
|
3
|
+
*
|
|
4
|
+
* Every colour in this design system is a Panda token whose *value* is a bare
|
|
5
|
+
* CSS custom property — `boxBgPrimary` resolves to `var(--hopper-box-primary-bg)`.
|
|
6
|
+
* Nothing here defines a colour. The host application is what sets those custom
|
|
7
|
+
* properties at runtime (from a theme picker, a database, a `<style>` block, a
|
|
8
|
+
* stylesheet — the system does not care), and that indirection is the whole
|
|
9
|
+
* reason a single component library can wear two products' branding.
|
|
10
|
+
*
|
|
11
|
+
* Two consequences worth internalising before changing anything below:
|
|
12
|
+
*
|
|
13
|
+
* 1. **A token with no matching custom property renders as nothing.** There is
|
|
14
|
+
* no fallback colour by design — a silent black-on-black box is a louder bug
|
|
15
|
+
* than a silent wrong-shade box, and it surfaces during development instead
|
|
16
|
+
* of in production. Consumers MUST define every property this file names.
|
|
17
|
+
* `requiredCssCustomProperties()` exists so a consumer can assert that.
|
|
18
|
+
*
|
|
19
|
+
* 2. **The names are public API.** A host app's theme data keys off them. Adding
|
|
20
|
+
* a token is backwards-compatible; renaming or removing one silently breaks
|
|
21
|
+
* whatever was painting it, because CSS has no import errors.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** A Panda token name mapped to the CSS custom-property suffix it reads. */
|
|
25
|
+
type TokenMap = Record<string, string>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `<panda token name>` → `<custom property suffix>`.
|
|
29
|
+
*
|
|
30
|
+
* The suffix is everything after the prefix, so `"box-primary-bg"` becomes
|
|
31
|
+
* `var(--hopper-box-primary-bg)` at the default prefix. The two naming schemes
|
|
32
|
+
* differ on purpose and are not derivable from each other: token names are
|
|
33
|
+
* camelCase and read component-first (`boxBgPrimary`), custom properties are
|
|
34
|
+
* kebab-case and read scope-first (`box-primary-bg`). Keeping the mapping
|
|
35
|
+
* explicit is what lets either side be renamed without touching the other.
|
|
36
|
+
*/
|
|
37
|
+
const COLOR_TOKENS: TokenMap = {
|
|
38
|
+
// Text that carries meaning on its own — errors, warnings, emphasis.
|
|
39
|
+
textPop: "text-pop-text",
|
|
40
|
+
textError: "text-error-text",
|
|
41
|
+
textWarning: "text-warning-text",
|
|
42
|
+
|
|
43
|
+
// Text on each surface. Pair these with the matching `boxBg*` — see
|
|
44
|
+
// TEXT_BACKGROUND_PAIRS for which goes with which.
|
|
45
|
+
textMain: "box-main-text",
|
|
46
|
+
textPrimary: "box-primary-text",
|
|
47
|
+
textSecondary: "box-secondary-text",
|
|
48
|
+
textAccent: "box-accent-text",
|
|
49
|
+
|
|
50
|
+
// Arrows / carets.
|
|
51
|
+
arrowBgPrimary: "arrow-primary-bg",
|
|
52
|
+
arrowBgSecondary: "arrow-secondary-bg",
|
|
53
|
+
arrowBgAccent: "arrow-accent-bg",
|
|
54
|
+
arrowBorderPrimary: "arrow-primary-border",
|
|
55
|
+
arrowBorderSecondary: "arrow-secondary-border",
|
|
56
|
+
arrowBorderAccent: "arrow-accent-border",
|
|
57
|
+
|
|
58
|
+
// Surfaces.
|
|
59
|
+
boxBgMain: "box-main-bg",
|
|
60
|
+
boxBgPrimary: "box-primary-bg",
|
|
61
|
+
boxBgSecondary: "box-secondary-bg",
|
|
62
|
+
boxBgAccent: "box-accent-bg",
|
|
63
|
+
boxInfo: "box-info-bg",
|
|
64
|
+
|
|
65
|
+
// Borders.
|
|
66
|
+
borderBgPrimary: "box-primary-border",
|
|
67
|
+
borderBgSecondary: "box-secondary-border",
|
|
68
|
+
borderBgAccent: "box-accent-border",
|
|
69
|
+
|
|
70
|
+
// Shadows.
|
|
71
|
+
boxshadowBgPrimary: "shadow-primary-bg",
|
|
72
|
+
boxshadowBgSecondary: "shadow-secondary-bg",
|
|
73
|
+
boxshadowBgAccent: "shadow-accent-bg",
|
|
74
|
+
|
|
75
|
+
// Buttons.
|
|
76
|
+
buttonBgPrimary: "button-primary-bg",
|
|
77
|
+
buttonBgSecondary: "button-secondary-bg",
|
|
78
|
+
buttonBgAccent: "button-accent-bg",
|
|
79
|
+
buttonBgPrimaryHover: "button-primary-hover-bg",
|
|
80
|
+
buttonBgSecondaryHover: "button-secondary-hover-bg",
|
|
81
|
+
buttonBgAccentHover: "button-accent-hover-bg",
|
|
82
|
+
buttonTextPrimary: "button-primary-text",
|
|
83
|
+
buttonTextSecondary: "button-secondary-text",
|
|
84
|
+
buttonTextAccent: "button-accent-text",
|
|
85
|
+
buttonTextPrimaryHover: "button-primary-hover-text",
|
|
86
|
+
buttonTextSecondaryHover: "button-secondary-hover-text",
|
|
87
|
+
buttonTextAccentHover: "button-accent-hover-text",
|
|
88
|
+
buttonBgPlain: "button-plain-bg",
|
|
89
|
+
buttonTextPlain: "button-plain-text",
|
|
90
|
+
|
|
91
|
+
// Icons.
|
|
92
|
+
iconBgPrimary: "icon-primary-bg",
|
|
93
|
+
iconBgSecondary: "icon-secondary-bg",
|
|
94
|
+
iconBgAccent: "icon-accent-bg",
|
|
95
|
+
iconBgPrimaryHover: "icon-primary-hover-bg",
|
|
96
|
+
iconBgSecondaryHover: "icon-secondary-hover-bg",
|
|
97
|
+
iconBgAccentHover: "icon-accent-hover-bg",
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Host-provided *layout* properties, as `token name → [suffix, fallback]`.
|
|
102
|
+
*
|
|
103
|
+
* These differ from the colours in one way that matters: they carry a fallback,
|
|
104
|
+
* so a host that never sets them still renders something sensible rather than
|
|
105
|
+
* nothing. They are routed through the token layer anyway, for the same reason
|
|
106
|
+
* the colours are — a recipe writing `var(--hopper-…)` directly would ignore a
|
|
107
|
+
* consumer's chosen prefix, silently, and no amount of correct configuration on
|
|
108
|
+
* their side would fix it.
|
|
109
|
+
*/
|
|
110
|
+
const SIZE_TOKENS: Record<string, [suffix: string, fallback: string]> = {
|
|
111
|
+
/** Height budget for a dashboard widget; also caps dropdown menus. */
|
|
112
|
+
widgetBaseHeight: ["widget-base-height", "240px"],
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The host's typeface, as `token name → [suffix, fallback]` (NEH-289).
|
|
117
|
+
*
|
|
118
|
+
* `stonedog-theme` resolves a theme's fonts into `--<prefix>-font-family-*` and
|
|
119
|
+
* `--<prefix>-font-weight-*`. Until these tokens existed nothing in this package
|
|
120
|
+
* read them, so a themed typeface was emitted and inert — it stopped at the
|
|
121
|
+
* theme package's edge.
|
|
122
|
+
*
|
|
123
|
+
* They follow the SIZE_TOKENS pattern (a fallback) rather than the COLOR_TOKENS
|
|
124
|
+
* one (no fallback), and the asymmetry is deliberate. An undefined colour paints
|
|
125
|
+
* an invisible element, which is a louder bug than a wrong shade and is exactly
|
|
126
|
+
* what you want to trip over in development. Type is the opposite: an undefined
|
|
127
|
+
* font falls back to the browser's own face and the page stays readable. So
|
|
128
|
+
* these are NOT in `requiredCssCustomProperties()` — putting them there would
|
|
129
|
+
* break every existing host (none of them define these) for no safety gain, and
|
|
130
|
+
* would move the `required === colours` identity that the contract test pins on
|
|
131
|
+
* both sides.
|
|
132
|
+
*
|
|
133
|
+
* Consequence worth stating plainly: a host that says nothing about type keeps
|
|
134
|
+
* its own, and this stays purely additive.
|
|
135
|
+
*
|
|
136
|
+
* This package still owns SHAPE. The size scale, line height and density are
|
|
137
|
+
* unchanged and stay here; family and weight are BRAND, and this is only the
|
|
138
|
+
* wire that lets the theme deliver them.
|
|
139
|
+
*/
|
|
140
|
+
const FONT_FAMILY_TOKENS: Record<string, [suffix: string, fallback: string]> = {
|
|
141
|
+
/** Body copy, and every form control that would otherwise use the UA font. */
|
|
142
|
+
body: ["font-family-body", "inherit"],
|
|
143
|
+
/** Headings, so a theme can pair a display face with its body face. */
|
|
144
|
+
heading: ["font-family-heading", "inherit"],
|
|
145
|
+
/**
|
|
146
|
+
* Monospace. `inherit` would be wrong here — the whole point of asking for
|
|
147
|
+
* mono is not wanting the inherited proportional face — so it falls back to
|
|
148
|
+
* the system mono stack instead.
|
|
149
|
+
*/
|
|
150
|
+
mono: ["font-family-mono", "ui-monospace, SFMono-Regular, Menlo, monospace"],
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Weight steps, matching what the recipes already name.
|
|
155
|
+
*
|
|
156
|
+
* The union is closed on purpose (`stonedog-theme` owns it): a recipe needing a
|
|
157
|
+
* step outside these four is a change there first. The names deliberately match
|
|
158
|
+
* Panda's built-in `fontWeights` tokens, so every existing `fontWeight: "bold"`
|
|
159
|
+
* in a recipe starts reading the theme without a single call site moving —
|
|
160
|
+
* `fontWeight` resolves through this token category already.
|
|
161
|
+
*/
|
|
162
|
+
const FONT_WEIGHT_TOKENS: Record<string, [suffix: string, fallback: string]> = {
|
|
163
|
+
normal: ["font-weight-normal", "400"],
|
|
164
|
+
medium: ["font-weight-medium", "500"],
|
|
165
|
+
semibold: ["font-weight-semibold", "600"],
|
|
166
|
+
bold: ["font-weight-bold", "700"],
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/** Shared shape for the token maps that carry a fallback. */
|
|
170
|
+
function createFallbackTokens(
|
|
171
|
+
map: Record<string, [suffix: string, fallback: string]>,
|
|
172
|
+
prefix: string,
|
|
173
|
+
): Record<string, { value: string }> {
|
|
174
|
+
return Object.fromEntries(
|
|
175
|
+
Object.entries(map).map(([token, [suffix, fallback]]) => [
|
|
176
|
+
token,
|
|
177
|
+
{ value: `var(--${prefix}-${suffix}, ${fallback})` },
|
|
178
|
+
]),
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Panda `fonts` token definitions, bound to a custom-property prefix. */
|
|
183
|
+
export function createSemanticFonts(
|
|
184
|
+
prefix: string = DEFAULT_CSS_VAR_PREFIX,
|
|
185
|
+
): Record<string, { value: string }> {
|
|
186
|
+
return createFallbackTokens(FONT_FAMILY_TOKENS, prefix);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Panda `fontWeights` token definitions, bound to a custom-property prefix. */
|
|
190
|
+
export function createSemanticFontWeights(
|
|
191
|
+
prefix: string = DEFAULT_CSS_VAR_PREFIX,
|
|
192
|
+
): Record<string, { value: string }> {
|
|
193
|
+
return createFallbackTokens(FONT_WEIGHT_TOKENS, prefix);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Every Panda font-family token this preset defines. */
|
|
197
|
+
export function fontTokenNames(): string[] {
|
|
198
|
+
return Object.keys(FONT_FAMILY_TOKENS);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Every Panda font-weight token this preset defines. */
|
|
202
|
+
export function fontWeightTokenNames(): string[] {
|
|
203
|
+
return Object.keys(FONT_WEIGHT_TOKENS);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Panda size-token definitions, bound to a custom-property prefix. */
|
|
207
|
+
export function createSemanticSizes(
|
|
208
|
+
prefix: string = DEFAULT_CSS_VAR_PREFIX,
|
|
209
|
+
): Record<string, { value: string }> {
|
|
210
|
+
return Object.fromEntries(
|
|
211
|
+
Object.entries(SIZE_TOKENS).map(([token, [suffix, fallback]]) => [
|
|
212
|
+
token,
|
|
213
|
+
{ value: `var(--${prefix}-${suffix}, ${fallback})` },
|
|
214
|
+
]),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* The default custom-property prefix.
|
|
220
|
+
*
|
|
221
|
+
* `hopper` rather than something neutral because HopperGuard's theme engine
|
|
222
|
+
* already emits `--hopper-*` and a rename there would be a coordinated change
|
|
223
|
+
* across a running product's stored theme data. A second consumer that wants
|
|
224
|
+
* its own namespace passes `cssVarPrefix` — see `stonedogStylePreset`.
|
|
225
|
+
*/
|
|
226
|
+
export const DEFAULT_CSS_VAR_PREFIX = "hopper";
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Text/background pairings, for contrast checking.
|
|
230
|
+
*
|
|
231
|
+
* A theme is only usable if each text token has enough contrast against the
|
|
232
|
+
* surface it actually lands on, and that relationship is not inferable from the
|
|
233
|
+
* names — `textMain` sits on `boxBgMain`, but `buttonTextPlain` sits on
|
|
234
|
+
* `buttonBgPlain`, not on any `box*`. A contrast checker that guesses will pass
|
|
235
|
+
* themes that are unreadable in practice.
|
|
236
|
+
*/
|
|
237
|
+
export const TEXT_BACKGROUND_PAIRS: Readonly<Record<string, string>> = {
|
|
238
|
+
textMain: "boxBgMain",
|
|
239
|
+
textPrimary: "boxBgPrimary",
|
|
240
|
+
textSecondary: "boxBgSecondary",
|
|
241
|
+
textAccent: "boxBgAccent",
|
|
242
|
+
buttonTextPrimary: "buttonBgPrimary",
|
|
243
|
+
buttonTextSecondary: "buttonBgSecondary",
|
|
244
|
+
buttonTextAccent: "buttonBgAccent",
|
|
245
|
+
buttonTextPrimaryHover: "buttonBgPrimaryHover",
|
|
246
|
+
buttonTextSecondaryHover: "buttonBgSecondaryHover",
|
|
247
|
+
buttonTextAccentHover: "buttonBgAccentHover",
|
|
248
|
+
buttonTextPlain: "buttonBgPlain",
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/** The surface token a given text token is meant to be read against. */
|
|
252
|
+
export function getBackgroundForText(textToken: string): string | undefined {
|
|
253
|
+
return TEXT_BACKGROUND_PAIRS[textToken];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Every Panda colour token this preset defines. */
|
|
257
|
+
export function colorTokenNames(): string[] {
|
|
258
|
+
return Object.keys(COLOR_TOKENS);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Every CSS custom property a consumer must define for the system to render.
|
|
263
|
+
*
|
|
264
|
+
* Intended for a startup assertion or a theme-validation test: a theme missing
|
|
265
|
+
* one of these paints nothing, with no error anywhere.
|
|
266
|
+
*/
|
|
267
|
+
export function requiredCssCustomProperties(
|
|
268
|
+
prefix: string = DEFAULT_CSS_VAR_PREFIX,
|
|
269
|
+
): string[] {
|
|
270
|
+
return Object.values(COLOR_TOKENS).map((suffix) => `--${prefix}-${suffix}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Panda colour-token definitions, bound to a custom-property prefix. */
|
|
274
|
+
export function createSemanticColors(
|
|
275
|
+
prefix: string = DEFAULT_CSS_VAR_PREFIX,
|
|
276
|
+
): Record<string, { value: string }> {
|
|
277
|
+
return Object.fromEntries(
|
|
278
|
+
Object.entries(COLOR_TOKENS).map(([token, suffix]) => [
|
|
279
|
+
token,
|
|
280
|
+
{ value: `var(--${prefix}-${suffix})` },
|
|
281
|
+
]),
|
|
282
|
+
);
|
|
283
|
+
}
|