@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.
Files changed (147) hide show
  1. package/Component/Component.js +598 -0
  2. package/Component/Component.scenarios.js +88 -0
  3. package/Component/Component.test.js +717 -0
  4. package/Component/README.md +455 -0
  5. package/Component/index.js +3 -0
  6. package/Component/observeElementConnection.js +52 -0
  7. package/Component/observeElementConnection.test.js +121 -0
  8. package/Elem/Elem.js +304 -0
  9. package/Elem/Elem.test.js +679 -0
  10. package/Elem/README.md +373 -0
  11. package/Elem/index.js +1 -0
  12. package/FontWithASyntaxHighlighter-Regular.woff2 +0 -0
  13. package/LICENSE +21 -0
  14. package/README.md +413 -0
  15. package/components/BottomSheet/BottomSheet.js +192 -0
  16. package/components/BottomSheet/BottomSheet.lld.md +25 -0
  17. package/components/BottomSheet/README.md +66 -0
  18. package/components/BottomSheet/index.js +1 -0
  19. package/components/Button/Button.js +53 -0
  20. package/components/Button/Button.lld.md +21 -0
  21. package/components/Button/index.js +1 -0
  22. package/components/Calendar/Calendar.js +720 -0
  23. package/components/Calendar/Calendar.lld.md +22 -0
  24. package/components/Calendar/CalendarEvent.js +102 -0
  25. package/components/Calendar/Toolbar.js +78 -0
  26. package/components/Calendar/index.js +2 -0
  27. package/components/Calendar/utils.js +56 -0
  28. package/components/Code/Code.js +84 -0
  29. package/components/Code/Code.lld.md +21 -0
  30. package/components/Code/index.js +1 -0
  31. package/components/ColorPicker/ColorPicker.js +445 -0
  32. package/components/ColorPicker/ColorPicker.lld.md +21 -0
  33. package/components/ColorPicker/index.js +1 -0
  34. package/components/ColorPicker/svg.js +5 -0
  35. package/components/Dialog/Dialog.js +278 -0
  36. package/components/Dialog/Dialog.lld.md +20 -0
  37. package/components/Dialog/README.md +96 -0
  38. package/components/Dialog/index.js +1 -0
  39. package/components/Form/Form.js +257 -0
  40. package/components/Form/Form.lld.md +21 -0
  41. package/components/Form/README.md +87 -0
  42. package/components/Form/index.js +1 -0
  43. package/components/Icon/Icon.js +54 -0
  44. package/components/Icon/Icon.lld.md +21 -0
  45. package/components/Icon/index.js +1 -0
  46. package/components/Input/Input.js +173 -0
  47. package/components/Input/Input.lld.md +28 -0
  48. package/components/Input/README.md +97 -0
  49. package/components/Input/index.js +2 -0
  50. package/components/Input/utils.js +122 -0
  51. package/components/Keyboard/Key.js +38 -0
  52. package/components/Keyboard/Keyboard.js +173 -0
  53. package/components/Keyboard/Keyboard.lld.md +21 -0
  54. package/components/Keyboard/index.js +1 -0
  55. package/components/Label/Label.js +214 -0
  56. package/components/Label/Label.lld.md +20 -0
  57. package/components/Label/index.js +1 -0
  58. package/components/Link/Link.js +43 -0
  59. package/components/Link/Link.lld.md +15 -0
  60. package/components/Link/index.js +1 -0
  61. package/components/List/List.js +82 -0
  62. package/components/List/List.lld.md +19 -0
  63. package/components/List/index.js +1 -0
  64. package/components/Menu/Menu.js +93 -0
  65. package/components/Menu/Menu.lld.md +15 -0
  66. package/components/Menu/index.js +1 -0
  67. package/components/Notify/Notify.js +96 -0
  68. package/components/Notify/Notify.lld.md +20 -0
  69. package/components/Notify/index.js +1 -0
  70. package/components/Page/Page.js +67 -0
  71. package/components/Page/Page.lld.md +20 -0
  72. package/components/Page/index.js +1 -0
  73. package/components/Popover/Popover.js +175 -0
  74. package/components/Popover/Popover.lld.md +19 -0
  75. package/components/Popover/index.js +1 -0
  76. package/components/RadioButton/RadioButton.js +108 -0
  77. package/components/RadioButton/RadioButton.lld.md +15 -0
  78. package/components/RadioButton/index.js +1 -0
  79. package/components/Router/README.md +160 -0
  80. package/components/Router/Router.js +150 -0
  81. package/components/Router/Router.lld.md +31 -0
  82. package/components/Router/View.js +15 -0
  83. package/components/Router/index.js +2 -0
  84. package/components/Router/utils.js +17 -0
  85. package/components/Select/README.md +88 -0
  86. package/components/Select/Select.js +74 -0
  87. package/components/Select/Select.lld.md +20 -0
  88. package/components/Select/index.js +1 -0
  89. package/components/Table/README.md +94 -0
  90. package/components/Table/Table.js +171 -0
  91. package/components/Table/Table.lld.md +21 -0
  92. package/components/Table/index.js +1 -0
  93. package/components/TagList/Tag.js +84 -0
  94. package/components/TagList/TagList.js +118 -0
  95. package/components/TagList/TagList.lld.md +30 -0
  96. package/components/TagList/design.excalidraw.png +0 -0
  97. package/components/TagList/index.js +2 -0
  98. package/components/Tooltip/Tooltip.js +139 -0
  99. package/components/Tooltip/Tooltip.lld.md +22 -0
  100. package/components/Tooltip/index.js +1 -0
  101. package/components/TooltipWrapper/TooltipWrapper.js +89 -0
  102. package/components/TooltipWrapper/TooltipWrapper.lld.md +21 -0
  103. package/components/TooltipWrapper/index.js +1 -0
  104. package/components/Whiteboard/Whiteboard.js +198 -0
  105. package/components/Whiteboard/Whiteboard.lld.md +35 -0
  106. package/components/Whiteboard/index.js +1 -0
  107. package/components/index.js +27 -0
  108. package/eslint.config.cjs +118 -0
  109. package/index.d.ts +635 -0
  110. package/index.js +19 -0
  111. package/package.json +123 -0
  112. package/plugins/asText.js +38 -0
  113. package/plugins/loadPlugins.js +5 -0
  114. package/plugins/markdownLoader.js +121 -0
  115. package/prettier.config.cjs +7 -0
  116. package/spellcheck.config.cjs +227 -0
  117. package/styled/README.md +329 -0
  118. package/styled/appendStyles.js +26 -0
  119. package/styled/appendStyles.test.js +45 -0
  120. package/styled/index.js +4 -0
  121. package/styled/shimCSS.js +31 -0
  122. package/styled/shimCSS.test.js +103 -0
  123. package/styled/styled.js +91 -0
  124. package/styled/styled.test.js +586 -0
  125. package/styled/themeStyles.js +36 -0
  126. package/styled/themeStyles.test.js +135 -0
  127. package/test-setup.js +123 -0
  128. package/theme/.test.js +69 -0
  129. package/theme/README.md +607 -0
  130. package/theme/button.js +100 -0
  131. package/theme/code.js +123 -0
  132. package/theme/colors.js +42 -0
  133. package/theme/fonts.js +42 -0
  134. package/theme/index.js +33 -0
  135. package/theme/input.js +64 -0
  136. package/theme/page.js +208 -0
  137. package/theme/scrollbar.js +24 -0
  138. package/theme/table.js +53 -0
  139. package/utils/README.md +176 -0
  140. package/utils/browser.js +92 -0
  141. package/utils/class.js +30 -0
  142. package/utils/color.js +81 -0
  143. package/utils/data.js +164 -0
  144. package/utils/element.js +55 -0
  145. package/utils/index.js +7 -0
  146. package/utils/rand.js +12 -0
  147. package/utils/string.js +72 -0
@@ -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
+ ```
@@ -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
+ `;