@vanilla-bean/components 1.0.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/Component/Component.js +598 -0
- package/Component/Component.scenarios.js +88 -0
- package/Component/Component.test.js +717 -0
- package/Component/README.md +455 -0
- package/Component/index.js +3 -0
- package/Component/observeElementConnection.js +52 -0
- package/Component/observeElementConnection.test.js +121 -0
- package/Elem/Elem.js +304 -0
- package/Elem/Elem.test.js +679 -0
- package/Elem/README.md +373 -0
- package/Elem/index.js +1 -0
- package/FontWithASyntaxHighlighter-Regular.woff2 +0 -0
- package/LICENSE +21 -0
- package/README.md +413 -0
- package/components/BottomSheet/BottomSheet.js +192 -0
- package/components/BottomSheet/BottomSheet.lld.md +25 -0
- package/components/BottomSheet/README.md +66 -0
- package/components/BottomSheet/index.js +1 -0
- package/components/Button/Button.js +53 -0
- package/components/Button/Button.lld.md +21 -0
- package/components/Button/index.js +1 -0
- package/components/Calendar/Calendar.js +720 -0
- package/components/Calendar/Calendar.lld.md +22 -0
- package/components/Calendar/CalendarEvent.js +102 -0
- package/components/Calendar/Toolbar.js +78 -0
- package/components/Calendar/index.js +2 -0
- package/components/Calendar/utils.js +56 -0
- package/components/Code/Code.js +84 -0
- package/components/Code/Code.lld.md +21 -0
- package/components/Code/index.js +1 -0
- package/components/ColorPicker/ColorPicker.js +445 -0
- package/components/ColorPicker/ColorPicker.lld.md +21 -0
- package/components/ColorPicker/index.js +1 -0
- package/components/ColorPicker/svg.js +5 -0
- package/components/Dialog/Dialog.js +278 -0
- package/components/Dialog/Dialog.lld.md +20 -0
- package/components/Dialog/README.md +96 -0
- package/components/Dialog/index.js +1 -0
- package/components/Form/Form.js +257 -0
- package/components/Form/Form.lld.md +21 -0
- package/components/Form/README.md +87 -0
- package/components/Form/index.js +1 -0
- package/components/Icon/Icon.js +54 -0
- package/components/Icon/Icon.lld.md +21 -0
- package/components/Icon/index.js +1 -0
- package/components/Input/Input.js +173 -0
- package/components/Input/Input.lld.md +28 -0
- package/components/Input/README.md +97 -0
- package/components/Input/index.js +2 -0
- package/components/Input/utils.js +122 -0
- package/components/Keyboard/Key.js +38 -0
- package/components/Keyboard/Keyboard.js +173 -0
- package/components/Keyboard/Keyboard.lld.md +21 -0
- package/components/Keyboard/index.js +1 -0
- package/components/Label/Label.js +214 -0
- package/components/Label/Label.lld.md +20 -0
- package/components/Label/index.js +1 -0
- package/components/Link/Link.js +43 -0
- package/components/Link/Link.lld.md +15 -0
- package/components/Link/index.js +1 -0
- package/components/List/List.js +82 -0
- package/components/List/List.lld.md +19 -0
- package/components/List/index.js +1 -0
- package/components/Menu/Menu.js +93 -0
- package/components/Menu/Menu.lld.md +15 -0
- package/components/Menu/index.js +1 -0
- package/components/Notify/Notify.js +96 -0
- package/components/Notify/Notify.lld.md +20 -0
- package/components/Notify/index.js +1 -0
- package/components/Page/Page.js +67 -0
- package/components/Page/Page.lld.md +20 -0
- package/components/Page/index.js +1 -0
- package/components/Popover/Popover.js +175 -0
- package/components/Popover/Popover.lld.md +19 -0
- package/components/Popover/index.js +1 -0
- package/components/RadioButton/RadioButton.js +108 -0
- package/components/RadioButton/RadioButton.lld.md +15 -0
- package/components/RadioButton/index.js +1 -0
- package/components/Router/README.md +160 -0
- package/components/Router/Router.js +150 -0
- package/components/Router/Router.lld.md +31 -0
- package/components/Router/View.js +15 -0
- package/components/Router/index.js +2 -0
- package/components/Router/utils.js +17 -0
- package/components/Select/README.md +88 -0
- package/components/Select/Select.js +74 -0
- package/components/Select/Select.lld.md +20 -0
- package/components/Select/index.js +1 -0
- package/components/Table/README.md +94 -0
- package/components/Table/Table.js +171 -0
- package/components/Table/Table.lld.md +21 -0
- package/components/Table/index.js +1 -0
- package/components/TagList/Tag.js +84 -0
- package/components/TagList/TagList.js +118 -0
- package/components/TagList/TagList.lld.md +30 -0
- package/components/TagList/design.excalidraw.png +0 -0
- package/components/TagList/index.js +2 -0
- package/components/Tooltip/Tooltip.js +139 -0
- package/components/Tooltip/Tooltip.lld.md +22 -0
- package/components/Tooltip/index.js +1 -0
- package/components/TooltipWrapper/TooltipWrapper.js +89 -0
- package/components/TooltipWrapper/TooltipWrapper.lld.md +21 -0
- package/components/TooltipWrapper/index.js +1 -0
- package/components/Whiteboard/Whiteboard.js +198 -0
- package/components/Whiteboard/Whiteboard.lld.md +35 -0
- package/components/Whiteboard/index.js +1 -0
- package/components/index.js +27 -0
- package/eslint.config.cjs +118 -0
- package/index.d.ts +635 -0
- package/index.js +19 -0
- package/package.json +123 -0
- package/plugins/asText.js +38 -0
- package/plugins/loadPlugins.js +5 -0
- package/plugins/markdownLoader.js +121 -0
- package/prettier.config.cjs +7 -0
- package/spellcheck.config.cjs +227 -0
- package/styled/README.md +329 -0
- package/styled/appendStyles.js +26 -0
- package/styled/appendStyles.test.js +45 -0
- package/styled/index.js +4 -0
- package/styled/shimCSS.js +31 -0
- package/styled/shimCSS.test.js +103 -0
- package/styled/styled.js +91 -0
- package/styled/styled.test.js +586 -0
- package/styled/themeStyles.js +36 -0
- package/styled/themeStyles.test.js +135 -0
- package/test-setup.js +123 -0
- package/theme/.test.js +69 -0
- package/theme/README.md +607 -0
- package/theme/button.js +100 -0
- package/theme/code.js +123 -0
- package/theme/colors.js +42 -0
- package/theme/fonts.js +42 -0
- package/theme/index.js +33 -0
- package/theme/input.js +64 -0
- package/theme/page.js +208 -0
- package/theme/scrollbar.js +24 -0
- package/theme/table.js +53 -0
- package/utils/README.md +176 -0
- package/utils/browser.js +92 -0
- package/utils/class.js +30 -0
- package/utils/color.js +81 -0
- package/utils/data.js +164 -0
- package/utils/element.js +55 -0
- package/utils/index.js +7 -0
- package/utils/rand.js +12 -0
- package/utils/string.js +72 -0
package/theme/README.md
ADDED
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
# theme
|
|
2
|
+
|
|
3
|
+
Design tokens: colors, fonts, and component base styles, passed to every `styled()` call and available for direct use.
|
|
4
|
+
|
|
5
|
+
## The Default Aesthetic
|
|
6
|
+
|
|
7
|
+
VBC ships with an intentional design language: dark backgrounds, a teal accent palette, and a technical/monospace typographic voice. This is a position, not a placeholder. The components look the way they look because that's how they were designed.
|
|
8
|
+
|
|
9
|
+
That said, the theme system is layered. You can work at any layer depending on how much you want to change.
|
|
10
|
+
|
|
11
|
+
## Customizing the Theme
|
|
12
|
+
|
|
13
|
+
### Layer 1: Per-instance styles
|
|
14
|
+
|
|
15
|
+
For one-off overrides on a specific component instance, pass `styles` as an option:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
// Object form — applied as inline styles
|
|
19
|
+
new Button({ styles: { backgroundColor: '#your-brand', borderRadius: '4px' } });
|
|
20
|
+
|
|
21
|
+
// Function form — receives theme, generates scoped CSS
|
|
22
|
+
new Dialog({
|
|
23
|
+
styles: ({ colors }) => `
|
|
24
|
+
border-color: ${colors.purple};
|
|
25
|
+
& .header { color: ${colors.purple}; }
|
|
26
|
+
`,
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Layer 2: Per-class overrides with `styled()`
|
|
31
|
+
|
|
32
|
+
To apply a consistent override across all instances of a component type, create a styled subclass. The style function receives the VBC theme. Ignore it and use your own tokens if you prefer:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { styled, Button } from '@vanilla-bean/components';
|
|
36
|
+
|
|
37
|
+
const MY_BRAND = '#3d7aed';
|
|
38
|
+
|
|
39
|
+
const BrandButton = styled(
|
|
40
|
+
Button,
|
|
41
|
+
() => `
|
|
42
|
+
background: ${MY_BRAND};
|
|
43
|
+
border: none;
|
|
44
|
+
border-radius: 4px;
|
|
45
|
+
color: white;
|
|
46
|
+
|
|
47
|
+
&:hover { background: #2f62c4; }
|
|
48
|
+
`,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// BrandButton inherits all Button behavior; only the appearance changes
|
|
52
|
+
new BrandButton({ textContent: 'Save', onPointerPress: handleSave });
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This pattern applies to any component, including the named shorthand:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
const BrandDialog = styled.Dialog`
|
|
59
|
+
border-color: ${() => MY_BRAND};
|
|
60
|
+
background: #1a1a2e;
|
|
61
|
+
`;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Layer 3: Override theme tokens globally
|
|
65
|
+
|
|
66
|
+
The theme singleton is imported once and shared across all `styled()` calls. Mutate it before your components initialize and every component that uses that token picks up the change:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import { theme } from '@vanilla-bean/components';
|
|
70
|
+
import { TinyColor } from '@ctrl/tinycolor';
|
|
71
|
+
|
|
72
|
+
// Replace the accent color — affects every component that uses colors.teal
|
|
73
|
+
theme.colors.teal = new TinyColor('#3d7aed');
|
|
74
|
+
|
|
75
|
+
// Then import and use components as normal — they'll use your teal
|
|
76
|
+
import { Button, Dialog } from '@vanilla-bean/components';
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Mutate as many tokens as your design system requires. Because colors are plain properties on the object, this is straightforward JavaScript.
|
|
80
|
+
|
|
81
|
+
### Layer 4: Skip the theme system entirely
|
|
82
|
+
|
|
83
|
+
`styled()` is optional. Every component works without it. If you want to use your own CSS pipeline (CSS modules, Tailwind, a design token library), just don't call `styled()`. Write plain CSS that targets the component's tag or a class you add:
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
// Add your own class; target it in your stylesheet
|
|
87
|
+
new Button({ addClass: 'my-button', textContent: 'Save' });
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```css
|
|
91
|
+
/* your-styles.css */
|
|
92
|
+
.my-button {
|
|
93
|
+
background: var(--brand-primary);
|
|
94
|
+
border-radius: var(--radius-md);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or inject scoped styles directly via `appendStyles`:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
import { appendStyles } from '@vanilla-bean/components/styled';
|
|
102
|
+
|
|
103
|
+
appendStyles(
|
|
104
|
+
`
|
|
105
|
+
.my-button { background: #3d7aed; border-radius: 4px; }
|
|
106
|
+
`,
|
|
107
|
+
'my-button-styles',
|
|
108
|
+
);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The four layers compose freely. Per-instance `styles` always take precedence over class-level `styled()` CSS. Both are scoped and don't leak.
|
|
112
|
+
|
|
113
|
+
## Quick Start
|
|
114
|
+
|
|
115
|
+
### Basic Color Usage
|
|
116
|
+
|
|
117
|
+
Access colors directly or use within styled components:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
import { theme } from '@vanilla-bean/components';
|
|
121
|
+
|
|
122
|
+
// Direct color access
|
|
123
|
+
const primaryColor = theme.colors.blue;
|
|
124
|
+
console.log(primaryColor.toHexString()); // "#4a7ba7"
|
|
125
|
+
|
|
126
|
+
// With styled components
|
|
127
|
+
const StyledButton = styled.Button`
|
|
128
|
+
background: ${({ colors }) => colors.blue};
|
|
129
|
+
color: ${({ colors }) => colors.mostReadable(colors.blue, [colors.white, colors.black])};
|
|
130
|
+
|
|
131
|
+
&:hover {
|
|
132
|
+
background: ${({ colors }) => colors.darker(colors.blue)};
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Font Integration
|
|
138
|
+
|
|
139
|
+
Apply typography and iconography:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
// Typography in styled components
|
|
143
|
+
const CodeBlock = styled.Pre`
|
|
144
|
+
${({ fonts }) => fonts.code}
|
|
145
|
+
${({ fonts }) => fonts.kodeMono}
|
|
146
|
+
|
|
147
|
+
&.language-javascript {
|
|
148
|
+
font-palette: --vbc-javascript-theme;
|
|
149
|
+
}
|
|
150
|
+
`;
|
|
151
|
+
|
|
152
|
+
// Icons with Font Awesome
|
|
153
|
+
const IconButton = styled.Button`
|
|
154
|
+
&:before {
|
|
155
|
+
${({ fonts }) => fonts.fontAwesomeSolid}
|
|
156
|
+
content: "\\f015"; /* Home icon */
|
|
157
|
+
margin-right: 8px;
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Component Base Styles
|
|
163
|
+
|
|
164
|
+
Extend pre-built component styles:
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
// Apply base button styles with customization
|
|
168
|
+
const CustomButton = styled.Button`
|
|
169
|
+
${({ button }) => button}
|
|
170
|
+
background: ${({ colors }) => colors.purple};
|
|
171
|
+
border-radius: 8px;
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
// Mix multiple base styles
|
|
175
|
+
const FormCard = styled.Component`
|
|
176
|
+
background: ${({ colors }) => colors.darker(colors.gray)};
|
|
177
|
+
border: 1px solid ${({ colors }) => colors.darkest(colors.gray)};
|
|
178
|
+
padding: 24px;
|
|
179
|
+
border-radius: 6px;
|
|
180
|
+
|
|
181
|
+
input,
|
|
182
|
+
select,
|
|
183
|
+
textarea {
|
|
184
|
+
${({ input }) => input}
|
|
185
|
+
}
|
|
186
|
+
`;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Theme Integration
|
|
190
|
+
|
|
191
|
+
Use theme in Component options:
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
new Component({
|
|
195
|
+
styles: ({ colors, fonts }) => ({
|
|
196
|
+
color: colors.mostReadable(colors.blue, [colors.white, colors.black]),
|
|
197
|
+
...fonts.kodeMono,
|
|
198
|
+
background: colors.lighter(colors.gray),
|
|
199
|
+
padding: '16px',
|
|
200
|
+
borderRadius: '4px',
|
|
201
|
+
}),
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Color System
|
|
206
|
+
|
|
207
|
+
### Base Colors
|
|
208
|
+
|
|
209
|
+
9 carefully chosen base colors for comprehensive UI design:
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
const { colors } = theme;
|
|
213
|
+
|
|
214
|
+
// Primary palette
|
|
215
|
+
colors.blue; // #4a7ba7 - Primary actions, links
|
|
216
|
+
colors.green; // #5cb85c - Success states, positive actions
|
|
217
|
+
colors.red; // #d9534f - Errors, warnings, destructive actions
|
|
218
|
+
colors.orange; // #f0ad4e - Alerts, secondary warnings
|
|
219
|
+
colors.purple; // #5d4e75 - Special features, premium content
|
|
220
|
+
|
|
221
|
+
// Extended palette
|
|
222
|
+
colors.teal; // #5bc0de - Information, neutral actions
|
|
223
|
+
colors.yellow; // #f0e68c - Cautions, highlights
|
|
224
|
+
colors.pink; // #d63384 - Creative, playful elements
|
|
225
|
+
colors.gray; // #6c757d - Text, borders, backgrounds
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Lightness Variations
|
|
229
|
+
|
|
230
|
+
8 systematic lightness modifiers for consistent color relationships:
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
// Light variations (for backgrounds, subtle elements)
|
|
234
|
+
colors.whiteish(color); // 45% lighter - Near white variants
|
|
235
|
+
colors.lightest(color); // 40% lighter - Very light backgrounds
|
|
236
|
+
colors.lighter(color); // 27% lighter - Light backgrounds
|
|
237
|
+
colors.light(color); // 17% lighter - Subtle highlights
|
|
238
|
+
|
|
239
|
+
// Dark variations (for text, emphasis)
|
|
240
|
+
colors.dark(color); // 15% darker - Subtle emphasis
|
|
241
|
+
colors.darker(color); // 25% darker - Strong emphasis
|
|
242
|
+
colors.darkest(color); // 30% darker - Maximum contrast
|
|
243
|
+
colors.blackish(color); // 35% darker - Near black variants
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Usage examples:**
|
|
247
|
+
|
|
248
|
+
```js
|
|
249
|
+
// Create color hierarchies
|
|
250
|
+
const buttonStyles = ({ colors }) => `
|
|
251
|
+
background: ${colors.blue};
|
|
252
|
+
border: 1px solid ${colors.darker(colors.blue)};
|
|
253
|
+
color: ${colors.lightest(colors.blue)};
|
|
254
|
+
|
|
255
|
+
&:hover {
|
|
256
|
+
background: ${colors.light(colors.blue)};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
&:active {
|
|
260
|
+
background: ${colors.dark(colors.blue)};
|
|
261
|
+
}
|
|
262
|
+
`;
|
|
263
|
+
|
|
264
|
+
// Semantic color mapping
|
|
265
|
+
const statusColors = {
|
|
266
|
+
success: colors.green,
|
|
267
|
+
successBg: colors.lightest(colors.green),
|
|
268
|
+
warning: colors.orange,
|
|
269
|
+
warningBg: colors.lightest(colors.orange),
|
|
270
|
+
error: colors.red,
|
|
271
|
+
errorBg: colors.lightest(colors.red),
|
|
272
|
+
};
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Utility Colors & Accessibility
|
|
276
|
+
|
|
277
|
+
Special utility colors and WCAG compliance helpers:
|
|
278
|
+
|
|
279
|
+
```js
|
|
280
|
+
// Utility colors
|
|
281
|
+
colors.transparent; // Transparent - for overlays, hidden elements
|
|
282
|
+
colors.white; // Pure white - text on dark backgrounds
|
|
283
|
+
colors.black; // Pure black - text on light backgrounds
|
|
284
|
+
colors.superWhite; // #fefefe - slightly warmer white
|
|
285
|
+
colors.vantablack; // #0a0a0a - rich black alternative
|
|
286
|
+
|
|
287
|
+
// Accessibility functions
|
|
288
|
+
colors.mostReadable(baseColor, [colors.white, colors.black]);
|
|
289
|
+
// Returns the highest contrast color for optimal readability
|
|
290
|
+
|
|
291
|
+
colors.readability(color1, color2);
|
|
292
|
+
// Returns WCAG contrast ratio (1-21, higher is better)
|
|
293
|
+
|
|
294
|
+
colors.isReadable(color1, color2, { level: 'AA', size: 'normal' });
|
|
295
|
+
// Tests WCAG compliance (AA/AAA levels, normal/large text)
|
|
296
|
+
|
|
297
|
+
colors.random();
|
|
298
|
+
// Generates random color for placeholders, testing
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Accessibility examples:**
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
// Ensure accessible text contrast
|
|
305
|
+
const accessibleCard = ({ colors }) => {
|
|
306
|
+
const bg = colors.darker(colors.gray);
|
|
307
|
+
const textColor = colors.mostReadable(bg, [colors.white, colors.black]);
|
|
308
|
+
|
|
309
|
+
return `
|
|
310
|
+
background: ${bg};
|
|
311
|
+
color: ${textColor};
|
|
312
|
+
`;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// Validate color combinations
|
|
316
|
+
const validateDesign = (bg, text) => {
|
|
317
|
+
const ratio = colors.readability(bg, text);
|
|
318
|
+
const isAccessible = colors.isReadable(bg, text, { level: 'AA' });
|
|
319
|
+
|
|
320
|
+
console.log(`Contrast ratio: ${ratio.toFixed(2)}`);
|
|
321
|
+
console.log(`WCAG AA compliant: ${isAccessible}`);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
validateDesign(colors.blue, colors.white); // Good contrast
|
|
325
|
+
validateDesign(colors.light(colors.blue), colors.white); // May fail
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Typography & Fonts
|
|
329
|
+
|
|
330
|
+
### Primary Typography
|
|
331
|
+
|
|
332
|
+
Complete typography system with specialized fonts:
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
// Interface typography
|
|
336
|
+
fonts.kodeMono; // Kode Mono Variable - Primary interface font
|
|
337
|
+
// Includes: font-family, font-weight variations, font-feature-settings
|
|
338
|
+
|
|
339
|
+
// Code and syntax highlighting
|
|
340
|
+
fonts.code; // FontWithASyntaxHighlighter + syntax highlighting setup
|
|
341
|
+
// Includes: monospace font, syntax color palettes, ligatures
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Font usage:**
|
|
345
|
+
|
|
346
|
+
```js
|
|
347
|
+
// Interface elements
|
|
348
|
+
const UIComponent = styled.Component`
|
|
349
|
+
${({ fonts }) => fonts.kodeMono}
|
|
350
|
+
font-weight: 400;
|
|
351
|
+
font-size: 14px;
|
|
352
|
+
`;
|
|
353
|
+
|
|
354
|
+
// Code blocks with syntax highlighting
|
|
355
|
+
const CodeEditor = styled.Pre`
|
|
356
|
+
${({ fonts }) => fonts.code}
|
|
357
|
+
|
|
358
|
+
&.language-javascript {
|
|
359
|
+
font-palette: --vbc-javascript-theme;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
&.language-css {
|
|
363
|
+
font-palette: --vbc-css-theme;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
&.language-html {
|
|
367
|
+
font-palette: --vbc-html-theme;
|
|
368
|
+
}
|
|
369
|
+
`;
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Icon System
|
|
373
|
+
|
|
374
|
+
Complete Font Awesome 6 integration:
|
|
375
|
+
|
|
376
|
+
```js
|
|
377
|
+
// Base icon setup
|
|
378
|
+
fonts.fontAwesome; // Base Font Awesome styles and setup
|
|
379
|
+
|
|
380
|
+
// Icon weight variations
|
|
381
|
+
fonts.fontAwesomeSolid; // Solid icons (font-weight: 600)
|
|
382
|
+
fonts.fontAwesomeBrands; // Brand icons (font-weight: normal)
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Icon examples:**
|
|
386
|
+
|
|
387
|
+
```js
|
|
388
|
+
// Basic icons
|
|
389
|
+
const HomeButton = styled.Button`
|
|
390
|
+
&:before {
|
|
391
|
+
${({ fonts }) => fonts.fontAwesomeSolid}
|
|
392
|
+
content: "\\f015"; /* Home icon */
|
|
393
|
+
margin-right: 8px;
|
|
394
|
+
}
|
|
395
|
+
`;
|
|
396
|
+
|
|
397
|
+
// Brand icons
|
|
398
|
+
const SocialLink = styled.Link`
|
|
399
|
+
&:before {
|
|
400
|
+
${({ fonts }) => fonts.fontAwesomeBrands}
|
|
401
|
+
content: "\\f09b"; /* GitHub icon */
|
|
402
|
+
}
|
|
403
|
+
`;
|
|
404
|
+
|
|
405
|
+
// Complex icon layouts
|
|
406
|
+
const IconGrid = styled.Component`
|
|
407
|
+
.icon {
|
|
408
|
+
${({ fonts }) => fonts.fontAwesome}
|
|
409
|
+
|
|
410
|
+
&.solid {
|
|
411
|
+
${({ fonts }) => fonts.fontAwesomeSolid}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
&.brand {
|
|
415
|
+
${({ fonts }) => fonts.fontAwesomeBrands}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
`;
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Syntax Highlighting Themes
|
|
422
|
+
|
|
423
|
+
Built-in color palettes for code syntax highlighting:
|
|
424
|
+
|
|
425
|
+
```css
|
|
426
|
+
/* Available CSS custom properties */
|
|
427
|
+
--vbc-javascript-theme /* JavaScript syntax colors */
|
|
428
|
+
--vbc-html-theme /* HTML/markup syntax colors */
|
|
429
|
+
--vbc-css-theme /* CSS syntax colors */
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Component Styles
|
|
433
|
+
|
|
434
|
+
### Pre-Built Component Styles
|
|
435
|
+
|
|
436
|
+
Complete styling for standard UI elements:
|
|
437
|
+
|
|
438
|
+
```js
|
|
439
|
+
theme.button; // Button styling with states (hover, active, disabled)
|
|
440
|
+
theme.input; // Form input styling with validation states
|
|
441
|
+
theme.table; // Data table with hover effects and borders
|
|
442
|
+
theme.scrollbar; // Custom scrollbar styling to match theme
|
|
443
|
+
theme.code; // Code syntax highlighting configuration
|
|
444
|
+
theme.page; // Global page styles and typography reset
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Component style usage:**
|
|
448
|
+
|
|
449
|
+
```js
|
|
450
|
+
// Extend base button styles
|
|
451
|
+
const PrimaryButton = styled.Button`
|
|
452
|
+
${({ button }) => button}
|
|
453
|
+
background: ${({ colors }) => colors.blue};
|
|
454
|
+
|
|
455
|
+
&:hover {
|
|
456
|
+
background: ${({ colors }) => colors.darker(colors.blue)};
|
|
457
|
+
}
|
|
458
|
+
`;
|
|
459
|
+
|
|
460
|
+
// Apply input styling with custom validation
|
|
461
|
+
const ValidatedInput = styled.Input`
|
|
462
|
+
${({ input }) => input}
|
|
463
|
+
|
|
464
|
+
&.error {
|
|
465
|
+
border-color: ${({ colors }) => colors.red};
|
|
466
|
+
background: ${({ colors }) => colors.lightest(colors.red)};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
&.success {
|
|
470
|
+
border-color: ${({ colors }) => colors.green};
|
|
471
|
+
}
|
|
472
|
+
`;
|
|
473
|
+
|
|
474
|
+
// Styled data table
|
|
475
|
+
const DataTable = styled.Table`
|
|
476
|
+
${({ table }) => table}
|
|
477
|
+
|
|
478
|
+
th {
|
|
479
|
+
background: ${({ colors }) => colors.darker(colors.gray)};
|
|
480
|
+
color: ${({ colors }) => colors.white};
|
|
481
|
+
}
|
|
482
|
+
`;
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Global Page Styling
|
|
486
|
+
|
|
487
|
+
The `page` style provides comprehensive global styling:
|
|
488
|
+
|
|
489
|
+
**Typography hierarchy:**
|
|
490
|
+
|
|
491
|
+
- `h1`-`h6` headings with semantic markdown-style prefixes
|
|
492
|
+
- Consistent font sizing and spacing
|
|
493
|
+
- Responsive typography scaling
|
|
494
|
+
|
|
495
|
+
**Form elements:**
|
|
496
|
+
|
|
497
|
+
- `input`, `select`, `textarea` styling
|
|
498
|
+
- `button` base styles with hover states
|
|
499
|
+
- Form validation states
|
|
500
|
+
|
|
501
|
+
**Content elements:**
|
|
502
|
+
|
|
503
|
+
- `blockquote` styling with left border
|
|
504
|
+
- `code` inline and block styling
|
|
505
|
+
- `table` responsive design
|
|
506
|
+
- `a` link styling with hover effects
|
|
507
|
+
|
|
508
|
+
**Custom scrollbars:**
|
|
509
|
+
|
|
510
|
+
- Styled to match theme colors
|
|
511
|
+
- Consistent across all scrollable elements
|
|
512
|
+
|
|
513
|
+
```js
|
|
514
|
+
// Apply global page styles
|
|
515
|
+
const App = styled.Component`
|
|
516
|
+
${({ page }) => page}
|
|
517
|
+
|
|
518
|
+
// Override specific globals
|
|
519
|
+
h1 {
|
|
520
|
+
color: ${({ colors }) => colors.purple};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.custom-button {
|
|
524
|
+
${({ button }) => button}
|
|
525
|
+
background: transparent;
|
|
526
|
+
border: 2px solid ${({ colors }) => colors.blue};
|
|
527
|
+
}
|
|
528
|
+
`;
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
## API Reference
|
|
532
|
+
|
|
533
|
+
### Theme Object Structure
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
interface Theme {
|
|
537
|
+
colors: ColorSystem;
|
|
538
|
+
fonts: FontSystem;
|
|
539
|
+
|
|
540
|
+
// Component base styles (CSS strings)
|
|
541
|
+
button: string;
|
|
542
|
+
input: string;
|
|
543
|
+
table: string;
|
|
544
|
+
scrollbar: string;
|
|
545
|
+
code: string;
|
|
546
|
+
page: string;
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### ColorSystem Interface
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
interface ColorSystem {
|
|
554
|
+
// Base colors (TinyColor instances)
|
|
555
|
+
orange: TinyColor;
|
|
556
|
+
gray: TinyColor;
|
|
557
|
+
yellow: TinyColor;
|
|
558
|
+
green: TinyColor;
|
|
559
|
+
teal: TinyColor;
|
|
560
|
+
blue: TinyColor;
|
|
561
|
+
purple: TinyColor;
|
|
562
|
+
pink: TinyColor;
|
|
563
|
+
red: TinyColor;
|
|
564
|
+
|
|
565
|
+
// Utility colors
|
|
566
|
+
transparent: TinyColor;
|
|
567
|
+
white: TinyColor;
|
|
568
|
+
black: TinyColor;
|
|
569
|
+
superWhite: TinyColor;
|
|
570
|
+
vantablack: TinyColor;
|
|
571
|
+
|
|
572
|
+
// Lightness modifiers
|
|
573
|
+
whiteish(color: TinyColor): TinyColor;
|
|
574
|
+
lightest(color: TinyColor): TinyColor;
|
|
575
|
+
lighter(color: TinyColor): TinyColor;
|
|
576
|
+
light(color: TinyColor): TinyColor;
|
|
577
|
+
dark(color: TinyColor): TinyColor;
|
|
578
|
+
darker(color: TinyColor): TinyColor;
|
|
579
|
+
darkest(color: TinyColor): TinyColor;
|
|
580
|
+
blackish(color: TinyColor): TinyColor;
|
|
581
|
+
|
|
582
|
+
// Accessibility functions
|
|
583
|
+
mostReadable(baseColor: TinyColor, candidates: TinyColor[]): TinyColor;
|
|
584
|
+
readability(color1: TinyColor, color2: TinyColor): number;
|
|
585
|
+
isReadable(
|
|
586
|
+
color1: TinyColor,
|
|
587
|
+
color2: TinyColor,
|
|
588
|
+
options?: {
|
|
589
|
+
level?: 'AA' | 'AAA';
|
|
590
|
+
size?: 'normal' | 'large';
|
|
591
|
+
},
|
|
592
|
+
): boolean;
|
|
593
|
+
random(): TinyColor;
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### FontSystem Interface
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
interface FontSystem {
|
|
601
|
+
kodeMono: string; // Primary interface font CSS
|
|
602
|
+
code: string; // Code font with syntax highlighting setup
|
|
603
|
+
fontAwesome: string; // Base Font Awesome styles
|
|
604
|
+
fontAwesomeSolid: string; // Solid Font Awesome icons
|
|
605
|
+
fontAwesomeBrands: string; // Brand Font Awesome icons
|
|
606
|
+
}
|
|
607
|
+
```
|
package/theme/button.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export default ({ colors, fonts }) => `
|
|
2
|
+
display: inline-block;
|
|
3
|
+
padding: 4px 6px 6px 6px;
|
|
4
|
+
margin: 0;
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
text-decoration: none;
|
|
7
|
+
color: ${colors.white};
|
|
8
|
+
background-color: ${colors.blue};
|
|
9
|
+
outline-color: ${colors.lighter(colors.orange)};
|
|
10
|
+
text-align: center;
|
|
11
|
+
position: relative;
|
|
12
|
+
white-space: nowrap;
|
|
13
|
+
text-overflow: ellipsis;
|
|
14
|
+
overflow: visible;
|
|
15
|
+
border: none;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
touch-action: none;
|
|
18
|
+
|
|
19
|
+
&.disabled, &:disabled {
|
|
20
|
+
background-color: ${colors.gray};
|
|
21
|
+
cursor: no-drop;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&:before {
|
|
25
|
+
${fonts.fontAwesomeSolid};
|
|
26
|
+
|
|
27
|
+
position: relative;
|
|
28
|
+
pointer-events: none;
|
|
29
|
+
padding: 0 6px 0 3px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:after {
|
|
33
|
+
content: '';
|
|
34
|
+
position: absolute;
|
|
35
|
+
top: 0;
|
|
36
|
+
left: 0;
|
|
37
|
+
width: 100%;
|
|
38
|
+
height: 100%;
|
|
39
|
+
background-image: linear-gradient(to right, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.1) 0.05em, rgba(0, 0, 0, 0) 0.08em),
|
|
40
|
+
linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.2) 0.08em, rgba(0, 0, 0, 0) 0.16em),
|
|
41
|
+
linear-gradient(to bottom, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.1) 0.05em, rgba(0, 0, 0, 0) 0.08em),
|
|
42
|
+
linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.2) 0.2em, rgba(0, 0, 0, 0) 0.25em);
|
|
43
|
+
pointer-events: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&:hover, &:focus {
|
|
47
|
+
top: -1px;
|
|
48
|
+
|
|
49
|
+
&:after {
|
|
50
|
+
background-image: linear-gradient(
|
|
51
|
+
to right,
|
|
52
|
+
rgba(0, 0, 0, 0.3),
|
|
53
|
+
rgba(0, 0, 0, 0.1) 0.05em,
|
|
54
|
+
rgba(0, 0, 0, 0) 0.08em
|
|
55
|
+
),
|
|
56
|
+
linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.2) 0.08em, rgba(0, 0, 0, 0) 0.16em),
|
|
57
|
+
linear-gradient(to bottom, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.1) 0.05em, rgba(0, 0, 0, 0) 0.08em),
|
|
58
|
+
linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.2) 0.24em, rgba(0, 0, 0, 0) 0.28em);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
&:active,
|
|
62
|
+
&.active,
|
|
63
|
+
&.pressed,
|
|
64
|
+
&.disabled,
|
|
65
|
+
&:disabled {
|
|
66
|
+
top: 1px;
|
|
67
|
+
|
|
68
|
+
&:before {
|
|
69
|
+
top: 1px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
&:after {
|
|
73
|
+
background-image: linear-gradient(
|
|
74
|
+
to right,
|
|
75
|
+
rgba(0, 0, 0, 0.5),
|
|
76
|
+
rgba(0, 0, 0, 0.3) 0.05em,
|
|
77
|
+
rgba(0, 0, 0, 0.2) 0.08em
|
|
78
|
+
),
|
|
79
|
+
linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.2) 0.08em, rgba(0, 0, 0, 0) 0.16em),
|
|
80
|
+
linear-gradient(to bottom, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.1) 0.05em, rgba(0, 0, 0, 0) 0.08em),
|
|
81
|
+
linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.2) 0.08em, rgba(0, 0, 0, 0) 0.16em);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
&.pressed {
|
|
85
|
+
box-shadow: 0 0 7px 3px rgba(0, 0, 0, 40%), inset 0 0 7px 3px rgba(0, 0, 0, 40%);
|
|
86
|
+
}
|
|
87
|
+
&.selected {
|
|
88
|
+
outline: 2px dashed ${colors.red};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&:empty, &.icon {
|
|
92
|
+
width: 30px;
|
|
93
|
+
height: 30px;
|
|
94
|
+
padding: 3px 0 8px 0;
|
|
95
|
+
|
|
96
|
+
&:before {
|
|
97
|
+
padding: 0;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
`;
|