@dodlhuat/basix 1.3.4 → 1.4.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 (82) hide show
  1. package/README.md +160 -18
  2. package/css/checkbox.scss +15 -2
  3. package/css/color-picker.scss +278 -0
  4. package/css/datepicker.scss +5 -3
  5. package/css/push-menu.scss +7 -0
  6. package/css/stepper.scss +1 -1
  7. package/css/style.css +291 -6
  8. package/css/style.css.map +1 -1
  9. package/css/style.min.css +1 -1
  10. package/css/style.min.css.map +1 -1
  11. package/css/style.scss +1 -0
  12. package/css/table.scss +33 -0
  13. package/js/bottom-sheet.d.ts +0 -2
  14. package/js/bottom-sheet.js +0 -2
  15. package/js/calendar.d.ts +9 -17
  16. package/js/calendar.js +4 -7
  17. package/js/carousel.d.ts +0 -2
  18. package/js/carousel.js +1 -3
  19. package/js/chart.d.ts +6 -14
  20. package/js/chart.js +3 -5
  21. package/js/code-viewer.d.ts +2 -1
  22. package/js/code-viewer.js +8 -2
  23. package/js/color-picker.d.ts +50 -0
  24. package/js/color-picker.js +291 -0
  25. package/js/context-menu.d.ts +0 -3
  26. package/js/context-menu.js +0 -3
  27. package/js/datepicker.d.ts +0 -4
  28. package/js/datepicker.js +0 -1
  29. package/js/docs-nav.js +1 -0
  30. package/js/dropdown.d.ts +1 -4
  31. package/js/dropdown.js +0 -1
  32. package/js/editor.d.ts +4 -3
  33. package/js/editor.js +50 -35
  34. package/js/file-uploader.d.ts +0 -4
  35. package/js/file-uploader.js +0 -1
  36. package/js/flyout-menu.d.ts +0 -2
  37. package/js/flyout-menu.js +0 -1
  38. package/js/gallery.d.ts +1 -4
  39. package/js/gallery.js +18 -23
  40. package/js/group-picker.d.ts +0 -5
  41. package/js/group-picker.js +6 -8
  42. package/js/lightbox.d.ts +1 -4
  43. package/js/lightbox.js +6 -10
  44. package/js/modal.d.ts +0 -2
  45. package/js/modal.js +0 -1
  46. package/js/popover.d.ts +0 -3
  47. package/js/popover.js +0 -2
  48. package/js/position.d.ts +0 -13
  49. package/js/position.js +0 -12
  50. package/js/push-menu.d.ts +0 -3
  51. package/js/push-menu.js +6 -11
  52. package/js/range-slider.d.ts +0 -1
  53. package/js/range-slider.js +2 -3
  54. package/js/scroll.d.ts +0 -2
  55. package/js/scroll.js +5 -6
  56. package/js/scrollbar.d.ts +2 -10
  57. package/js/scrollbar.js +25 -42
  58. package/js/select.d.ts +2 -3
  59. package/js/select.js +6 -9
  60. package/js/sidebar-nav.d.ts +4 -15
  61. package/js/sidebar-nav.js +21 -35
  62. package/js/stepper.d.ts +5 -2
  63. package/js/stepper.js +42 -6
  64. package/js/table.d.ts +1 -56
  65. package/js/table.js +4 -56
  66. package/js/tabs.d.ts +1 -32
  67. package/js/tabs.js +7 -39
  68. package/js/theme.d.ts +0 -44
  69. package/js/theme.js +0 -44
  70. package/js/timepicker.d.ts +1 -4
  71. package/js/timepicker.js +1 -2
  72. package/js/toast.d.ts +0 -2
  73. package/js/toast.js +0 -1
  74. package/js/tooltip.d.ts +0 -4
  75. package/js/tooltip.js +0 -1
  76. package/js/tree.d.ts +0 -3
  77. package/js/tree.js +9 -11
  78. package/js/utils.d.ts +0 -10
  79. package/js/utils.js +2 -42
  80. package/js/virtual-dropdown.d.ts +3 -5
  81. package/js/virtual-dropdown.js +17 -66
  82. package/package.json +1 -1
package/README.md CHANGED
@@ -1,10 +1,38 @@
1
- # Basix 1.3.4
1
+ # Basix 1.4.0
2
2
 
3
3
  Basix is intended as a starter for the rapid development of a design. Each design element can be added individually to
4
4
  include only the data required. It is using plain javascript / typescript and therefore is not dependent on any plugin.
5
5
 
6
6
  A demo can be found here: <a href="http://www.andibauer.at/basix/" target="_blank">http://www.andibauer.at/basix/</a>
7
7
 
8
+ ---
9
+
10
+ ## Migration Guide
11
+
12
+ ### 1.3.5 → 1.4.0
13
+
14
+ #### Breaking changes
15
+
16
+ **`TimeSpanPicker` — option `toString` renamed to `toLabel`**
17
+
18
+ The `toString` option shadowed `Object.prototype.toString` and is now renamed to `toLabel`. Update any call sites that pass this option:
19
+
20
+ ```js
21
+ // Before (1.3.5)
22
+ new TimeSpanPicker('container', { toString: 'Bis' });
23
+
24
+ // After (1.4.0)
25
+ new TimeSpanPicker('container', { toLabel: 'Bis' });
26
+ ```
27
+
28
+ #### Bug fixes
29
+
30
+ - **`MasonryGallery`** — the `reload` option was previously hard-coded to `2` and ignored. It now respects the value you pass. The default remains `2`, so existing usage without the option is unaffected.
31
+ - **`Tabs`** — `destroy()` no longer operates on detached DOM nodes. Event listeners are now cleaned up via `AbortController`.
32
+ - **`gallery`** — image-load errors no longer cause an unhandled promise rejection; they are logged to `console.error` instead.
33
+
34
+ ---
35
+
8
36
  ### Benefits
9
37
 
10
38
  * lightweight
@@ -479,8 +507,19 @@ The Tabs component creates accessible tabbed interfaces. Supports horizontal/ver
479
507
 
480
508
  | Option | Type | Default | Description |
481
509
  |---|---|---|---|
482
- | `layout` | string | `'horizontal'` | Layout of the tabs, either `'horizontal'` or `'vertical'` |
483
- | `defaultTab` | integer | `0` | Index of the default active tab (0-based) |
510
+ | `layout` | string | `'horizontal'` | `'horizontal'` or `'vertical'` |
511
+ | `defaultTab` | number | `0` | Index of the initially active tab (0-based) |
512
+ | `menuPos` | string | `'top'` / `'left'` | Tab bar position: `'top'`, `'bottom'`, `'left'`, `'right'`. Defaults to `'top'` for horizontal layout, `'left'` for vertical. |
513
+ | `onChange` | function | — | Callback fired on tab change: `(index: number) => void` |
514
+
515
+ | Method | Description |
516
+ |---|---|
517
+ | `goToTab(index)` | Programmatically activate a tab and focus it |
518
+ | `getCurrentTab()` | Returns the index of the currently active tab |
519
+ | `getTabCount()` | Returns the total number of tabs |
520
+ | `enableTab(index)` | Re-enables a disabled tab |
521
+ | `disableTab(index)` | Disables a tab; if it was active, moves focus to the next enabled tab |
522
+ | `destroy()` | Removes event listeners and cleans up ARIA attributes |
484
523
 
485
524
  ### Timeline
486
525
 
@@ -788,7 +827,7 @@ const picker = new TimeSpanPicker('my-container', {
788
827
  defaultStart: '09:00',
789
828
  defaultEnd: '17:00',
790
829
  fromString: 'Von', // optional — defaults to 'From'
791
- toString: 'Bis', // optional — defaults to 'To'
830
+ toLabel: 'Bis', // optional — defaults to 'To'
792
831
  onChange: (start, end) => console.log(start, end),
793
832
  });
794
833
 
@@ -799,6 +838,36 @@ picker.isValid(); // boolean
799
838
  picker.destroy();
800
839
  ```
801
840
 
841
+ ### Color Picker
842
+
843
+ The ColorPicker component renders a canvas-based HSB color picker with a hue slider and hex/RGB inputs. It builds its own DOM inside any container element and is fully mobile-friendly — drag gestures on the color field do not trigger page scroll.
844
+
845
+ ``` js
846
+ import { ColorPicker } from '@dodlhuat/basix/js/color-picker.js';
847
+
848
+ const picker = new ColorPicker('#my-picker', {
849
+ value: '#3d63dd',
850
+ onChange: (hex, rgb) => {
851
+ console.log(hex); // '#3d63dd'
852
+ console.log(rgb.r, rgb.g, rgb.b); // 61, 99, 221
853
+ },
854
+ });
855
+
856
+ picker.getValue(); // current hex string, e.g. '#3d63dd'
857
+ picker.setValue('#ff6b6b'); // set color programmatically
858
+ picker.destroy(); // removes listeners, disconnects ResizeObserver, clears DOM
859
+ ```
860
+
861
+ ``` html
862
+ <!-- Any empty container works — the component builds its own markup inside -->
863
+ <div id="my-picker"></div>
864
+ ```
865
+
866
+ | Option | Type | Default | Description |
867
+ |---|---|---|---|
868
+ | `value` | string | `'#ff0000'` | Initial hex color (e.g. `'#3d63dd'`). Must be a 6-digit hex string. |
869
+ | `onChange` | function | — | Called on every color change: `(hex: string, rgb: { r, g, b }) => void` |
870
+
802
871
  ### Select
803
872
 
804
873
  The Select component wraps a native `<select>` element with custom Basix styling. Supports single and multi-select.
@@ -813,9 +882,11 @@ The Select component wraps a native `<select>` element with custom Basix styling
813
882
  ``` js
814
883
  const sel = new Select('#my-select');
815
884
  sel.value(); // returns selected value string, or string[] for multi-select
885
+ sel.destroy(); // removes the document click listener
816
886
 
817
- // Or initialise by passing the element directly
818
- Select.init(document.querySelector('#my-select'));
887
+ // Static convenience form returns a cleanup function to remove the document listener
888
+ const cleanup = Select.init(document.querySelector('#my-select'));
889
+ cleanup?.(); // call when the select is removed from the DOM
819
890
  ```
820
891
 
821
892
  ### Code Viewer
@@ -823,32 +894,103 @@ Select.init(document.querySelector('#my-select'));
823
894
  The CodeViewer component renders syntax-highlighted code blocks inside any container. Supports JavaScript, HTML, and CSS.
824
895
 
825
896
  ``` js
826
- new CodeViewer('#output', '<div class="card">Hello</div>', 'html');
897
+ const viewer = new CodeViewer('#output', '<div class="card">Hello</div>', 'html');
827
898
  new CodeViewer('#output', 'const x = 42;', 'javascript');
828
899
  new CodeViewer('#output', '.card { padding: 1rem; }', 'css');
900
+
901
+ viewer.destroy(); // removes event listeners and clears the container
829
902
  ```
830
903
 
831
904
  ### Editor
832
905
 
833
- The Editor component provides a contenteditable rich-text editing area with undo/redo, word count, and an optional side panel showing the raw HTML source and a live preview. Requires a `#editable` element in the DOM.
906
+ The Editor component provides a contenteditable rich-text editing area with undo/redo, word count, and an optional side panel showing the raw HTML source and a live preview. Internal elements are located by `data-editor` attributes scoped to a `root` container no globally unique IDs required, which makes multiple editors on the same page possible.
834
907
 
835
908
  ``` html
836
- <div id="editable" contenteditable="true"></div>
837
- <!-- Optional side panel elements -->
838
- <textarea id="code"></textarea>
839
- <div id="preview"></div>
840
- <div id="sidePanel"></div>
841
- <span id="wordCount"></span>
909
+ <div class="editor" id="my-editor">
910
+ <div class="editor-toolbar">
911
+ <button data-editor-action="undo" title="Undo">↩</button>
912
+ <button data-editor-action="redo" title="Redo">↪</button>
913
+ <button data-cmd="bold">B</button>
914
+ <button data-cmd="italic">I</button>
915
+ <button data-editor-action="link">Link</button>
916
+ <!-- … -->
917
+ </div>
918
+ <div class="editor-body">
919
+ <div class="editor-main">
920
+ <div data-editor="editable" class="editable" contenteditable="true"></div>
921
+ </div>
922
+ <!-- Full mode: add side panel with code + preview -->
923
+ <div data-editor="side-panel" class="editor-side">
924
+ <div class="side-tabs">
925
+ <button class="side-tab active" data-tab="code-panel">HTML</button>
926
+ <button class="side-tab" data-tab="preview-panel">Preview</button>
927
+ </div>
928
+ <div class="side-panels">
929
+ <div class="side-panel active" data-editor="code-panel">
930
+ <textarea data-editor="code"></textarea>
931
+ </div>
932
+ <div class="side-panel" data-editor="preview-panel">
933
+ <div data-editor="preview" class="preview-content"></div>
934
+ </div>
935
+ </div>
936
+ </div>
937
+ </div>
938
+ <div class="editor-footer">
939
+ <span data-editor="wordcount">0 words</span>
940
+ </div>
941
+ </div>
842
942
  ```
843
943
 
844
944
  ``` js
845
- // Full editor with side panel
846
- new Editor();
945
+ // Single editor
946
+ new Editor({ root: '#my-editor' });
947
+
948
+ // Simple mode — only requires [data-editor="editable"], hides the side panel
949
+ new Editor({ root: '#my-editor', simple: true });
950
+
951
+ // Multiple editors on the same page
952
+ const editor1 = new Editor({ root: '#editor-1' });
953
+ const editor2 = new Editor({ root: '#editor-2' });
847
954
 
848
- // Simple mode — hides the side panel permanently
849
- new Editor({ simple: true });
955
+ editor1.destroy();
850
956
  ```
851
957
 
958
+ #### Options
959
+
960
+ | Option | Type | Default | Description |
961
+ |---|---|---|---|
962
+ | `root` | string \| HTMLElement | `document.body` | Root container. Required when multiple editors coexist on the same page. |
963
+ | `simple` | boolean | `false` | Simple mode requires only `[data-editor="editable"]`. Full mode additionally requires `code`, `preview`, `side-panel`, `code-panel`, and `preview-panel` elements. |
964
+
965
+ #### `data-editor` elements
966
+
967
+ | Value | Element | Required |
968
+ |---|---|---|
969
+ | `editable` | `contenteditable` div — the writing area | always |
970
+ | `code` | `<textarea>` — HTML source view | full mode |
971
+ | `preview` | `<div>` — live preview | full mode |
972
+ | `side-panel` | Side panel container | full mode |
973
+ | `code-panel` | Code tab panel (matches `data-tab="code-panel"`) | full mode |
974
+ | `preview-panel` | Preview tab panel | full mode |
975
+ | `wordcount` | Word count display | optional |
976
+ | `image-file` | `<input type="file">` for image insertion | optional |
977
+
978
+ #### `data-editor-action` buttons
979
+
980
+ | Value | Description |
981
+ |---|---|
982
+ | `undo` | Triggers undo |
983
+ | `redo` | Triggers redo |
984
+ | `link` | Opens URL prompt for link insertion |
985
+ | `image` | Triggers the image file picker |
986
+ | `save` | Downloads content as an HTML file (Ctrl+S) |
987
+ | `clear` | Clears all content |
988
+ | `clean` | Strips formatting from the current selection |
989
+ | `toggle-code` | Shows / hides the HTML side panel |
990
+ | `apply-code` | Applies the HTML source panel content to the editable area |
991
+ | `sanitize-code` | Sanitizes the HTML source, then applies it to the editable area |
992
+ | `minify-code` | Minifies the HTML source (removes whitespace and newlines) |
993
+
852
994
  ### Custom Scrollbar
853
995
 
854
996
  The Scrollbar component creates custom-styled scrollbars. Supports pointer/touch dragging, track clicking, and automatic thumb sizing. Can be used with any class.
package/css/checkbox.scss CHANGED
@@ -6,6 +6,13 @@
6
6
  position: absolute;
7
7
  opacity: 0;
8
8
  width: auto;
9
+ appearance: none;
10
+ -webkit-appearance: none;
11
+ border: none;
12
+ box-shadow: none;
13
+ min-height: 0;
14
+ padding: 0;
15
+ background: transparent;
9
16
 
10
17
  & + label {
11
18
  position: relative;
@@ -29,7 +36,7 @@
29
36
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
30
37
  }
31
38
 
32
- &:hover + label::before {
39
+ &:not(:disabled):hover + label::before {
33
40
  border-color: var(--accent-color);
34
41
  }
35
42
 
@@ -58,11 +65,12 @@
58
65
  &:disabled + label {
59
66
  color: var(--secondary-text);
60
67
  cursor: not-allowed;
68
+ pointer-events: none;
61
69
  }
62
70
 
63
71
  &:disabled + label::before {
64
72
  background-color: var(--secondary-background);
65
- border-color: var(--disabled);
73
+ border-color: color-mix(in srgb, var(--secondary-text) 35%, transparent);
66
74
  box-shadow: none;
67
75
  }
68
76
 
@@ -70,4 +78,9 @@
70
78
  background-color: var(--disabled);
71
79
  border-color: var(--disabled);
72
80
  }
81
+
82
+ &:disabled:checked + label::after {
83
+ border-right-color: color-mix(in srgb, var(--primary-text) 40%, transparent);
84
+ border-bottom-color: color-mix(in srgb, var(--primary-text) 40%, transparent);
85
+ }
73
86
  }
@@ -0,0 +1,278 @@
1
+ @use "properties";
2
+ @use "parameters" as *;
3
+
4
+ $thumb-size: 18px;
5
+ $thumb-size-touch: 22px;
6
+ $track-height: 12px;
7
+ $cursor-size: 14px;
8
+ $preview-size: 40px;
9
+
10
+ .color-picker {
11
+ width: 100%;
12
+ background: var(--background);
13
+ border: 1px solid var(--divider);
14
+ border-radius: $radius-lg;
15
+ padding: $spacing;
16
+ box-sizing: border-box;
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: calc($spacing * 0.75);
20
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
21
+ }
22
+
23
+ // ─── Saturation / brightness field ────────────────────────────────────────────
24
+
25
+ .color-picker__field {
26
+ position: relative;
27
+ overflow: hidden;
28
+ height: 180px;
29
+ border-radius: $border-radius;
30
+ cursor: crosshair;
31
+ touch-action: none;
32
+ flex-shrink: 0;
33
+
34
+ @media (min-width: 380px) {
35
+ height: 200px;
36
+ }
37
+ }
38
+
39
+ .color-picker__canvas {
40
+ display: block;
41
+ width: 100%;
42
+ height: 100%;
43
+ }
44
+
45
+ .color-picker__cursor {
46
+ position: absolute;
47
+ width: $cursor-size;
48
+ height: $cursor-size;
49
+ border-radius: 50%;
50
+ border: 2px solid #fff;
51
+ box-shadow:
52
+ 0 0 0 1px rgba(0, 0, 0, 0.3),
53
+ 0 1px 4px rgba(0, 0, 0, 0.4);
54
+ transform: translate(-50%, -50%);
55
+ pointer-events: none;
56
+ transition: none;
57
+
58
+ &--dark {
59
+ border-color: rgba(0, 0, 0, 0.6);
60
+ }
61
+ }
62
+
63
+ // ─── Controls row: hue slider + preview ───────────────────────────────────────
64
+
65
+ .color-picker__controls {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: $spacing;
69
+ }
70
+
71
+ // Rainbow strip is the track div itself — input sits absolutely centred over it
72
+ // so the input never contributes to layout height.
73
+ .color-picker__hue-track {
74
+ flex: 1;
75
+ position: relative;
76
+ height: $track-height;
77
+ border-radius: calc($track-height / 2);
78
+ background: linear-gradient(
79
+ to right,
80
+ #f00 0%,
81
+ #ff0 17%,
82
+ #0f0 33%,
83
+ #0ff 50%,
84
+ #00f 67%,
85
+ #f0f 83%,
86
+ #f00 100%
87
+ );
88
+ }
89
+
90
+ .color-picker__hue-slider {
91
+ -webkit-appearance: none;
92
+ appearance: none;
93
+ position: absolute;
94
+ top: 50%;
95
+ left: 0;
96
+ transform: translateY(-50%);
97
+ width: 100%;
98
+ height: $thumb-size;
99
+ background: transparent;
100
+ cursor: pointer;
101
+ margin: 0;
102
+ padding: 0;
103
+ border: none;
104
+ outline: none;
105
+
106
+ &::-webkit-slider-runnable-track {
107
+ height: $thumb-size;
108
+ background: transparent;
109
+ }
110
+
111
+ &::-webkit-slider-thumb {
112
+ -webkit-appearance: none;
113
+ appearance: none;
114
+ width: $thumb-size;
115
+ height: $thumb-size;
116
+ border-radius: 50%;
117
+ background: #fff;
118
+ border: 2px solid #fff;
119
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
120
+ margin-top: 0;
121
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
122
+ }
123
+
124
+ &::-moz-range-track {
125
+ height: $thumb-size;
126
+ background: transparent;
127
+ }
128
+
129
+ // ── Moz thumb ─────────────────────────────────────────────────────────────
130
+ &::-moz-range-thumb {
131
+ width: $thumb-size;
132
+ height: $thumb-size;
133
+ border-radius: 50%;
134
+ background: #fff;
135
+ border: 2px solid #fff;
136
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
137
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
138
+ cursor: pointer;
139
+ }
140
+
141
+ &:focus {
142
+ outline: none;
143
+ }
144
+
145
+ &:focus-visible::-webkit-slider-thumb {
146
+ box-shadow:
147
+ 0 1px 3px rgba(0, 0, 0, 0.35),
148
+ 0 0 0 3px var(--accent-color-tint);
149
+ }
150
+
151
+ &:focus-visible::-moz-range-thumb {
152
+ box-shadow:
153
+ 0 1px 3px rgba(0, 0, 0, 0.35),
154
+ 0 0 0 3px var(--accent-color-tint);
155
+ }
156
+
157
+ &:hover::-webkit-slider-thumb {
158
+ transform: scale(1.15);
159
+ }
160
+
161
+ &:hover::-moz-range-thumb {
162
+ transform: scale(1.15);
163
+ }
164
+
165
+ &:active::-webkit-slider-thumb {
166
+ transform: scale(1.2);
167
+ }
168
+
169
+ &:active::-moz-range-thumb {
170
+ transform: scale(1.2);
171
+ }
172
+ }
173
+
174
+ .color-picker__preview {
175
+ width: $preview-size;
176
+ height: $preview-size;
177
+ border-radius: 50%;
178
+ border: 1px solid var(--divider);
179
+ flex-shrink: 0;
180
+ }
181
+
182
+ // ─── Text inputs ──────────────────────────────────────────────────────────────
183
+
184
+ .color-picker__inputs {
185
+ display: flex;
186
+ gap: calc($spacing * 0.5);
187
+ }
188
+
189
+ .color-picker__input-group {
190
+ display: flex;
191
+ flex-direction: column;
192
+ gap: 4px;
193
+ flex: 1;
194
+
195
+ &--hex {
196
+ flex: 2;
197
+ }
198
+
199
+ label {
200
+ font-size: 0.7rem;
201
+ font-weight: 500;
202
+ color: var(--secondary-text);
203
+ text-align: center;
204
+ line-height: 1;
205
+ user-select: none;
206
+ }
207
+ }
208
+
209
+ .color-picker__hex,
210
+ .color-picker__r,
211
+ .color-picker__g,
212
+ .color-picker__b {
213
+ width: 100%;
214
+ box-sizing: border-box;
215
+ padding: 0.35rem 0.4rem;
216
+ border: 1px solid var(--divider);
217
+ border-radius: $border-radius;
218
+ font-family: monospace;
219
+ font-size: 0.8rem;
220
+ text-align: center;
221
+ background: var(--secondary-background);
222
+ color: var(--primary-text);
223
+ transition: border-color 0.15s, box-shadow 0.15s;
224
+ outline: none;
225
+ -moz-appearance: textfield;
226
+
227
+ &::-webkit-inner-spin-button,
228
+ &::-webkit-outer-spin-button {
229
+ -webkit-appearance: none;
230
+ margin: 0;
231
+ }
232
+
233
+ &:focus {
234
+ border-color: var(--accent-color);
235
+ box-shadow: 0 0 0 2px var(--accent-color-tint);
236
+ }
237
+ }
238
+
239
+ // ─── Mobile ───────────────────────────────────────────────────────────────────
240
+
241
+ @media (max-width: 480px) {
242
+ .color-picker__field {
243
+ height: 160px;
244
+ }
245
+
246
+ .color-picker__hue-slider {
247
+ height: $thumb-size-touch;
248
+
249
+ &::-webkit-slider-runnable-track { height: $thumb-size-touch; }
250
+ &::-webkit-slider-thumb { width: $thumb-size-touch; height: $thumb-size-touch; }
251
+ &::-moz-range-track { height: $thumb-size-touch; }
252
+ &::-moz-range-thumb { width: $thumb-size-touch; height: $thumb-size-touch; }
253
+ }
254
+
255
+ .color-picker__hex,
256
+ .color-picker__r,
257
+ .color-picker__g,
258
+ .color-picker__b {
259
+ font-size: 0.75rem;
260
+ padding: 0.28rem 0.3rem;
261
+ }
262
+ }
263
+
264
+ @media (prefers-reduced-motion: reduce) {
265
+ .color-picker__hue-slider {
266
+ &::-webkit-slider-thumb,
267
+ &::-moz-range-thumb {
268
+ transition: none;
269
+ }
270
+ }
271
+
272
+ .color-picker__hex,
273
+ .color-picker__r,
274
+ .color-picker__g,
275
+ .color-picker__b {
276
+ transition: none;
277
+ }
278
+ }
@@ -104,8 +104,8 @@
104
104
  }
105
105
 
106
106
  .datepicker-day.in-range {
107
- background-color: var(--accent-color-lighten);
108
- color: var(--accent-color-text);
107
+ background-color: color-mix(in srgb, var(--accent-color) 15%, transparent);
108
+ color: var(--primary-text);
109
109
  border-radius: 0;
110
110
  }
111
111
 
@@ -128,7 +128,9 @@
128
128
  color: var(--accent-color);
129
129
  }
130
130
 
131
- .datepicker-day.selected.today {
131
+ .datepicker-day.selected.today,
132
+ .datepicker-day.range-start.today,
133
+ .datepicker-day.range-end.today {
132
134
  color: var(--accent-color-text);
133
135
  }
134
136
 
@@ -128,6 +128,13 @@ nav.push-menu {
128
128
 
129
129
  nav li {
130
130
  margin: 0;
131
+ list-style: none;
132
+ }
133
+
134
+ nav ul {
135
+ list-style: none;
136
+ padding: 0;
137
+ margin: 0;
131
138
  }
132
139
 
133
140
  nav a,
package/css/stepper.scss CHANGED
@@ -118,7 +118,7 @@ $connector-thickness: 2px;
118
118
  border-color: var(--accent-color);
119
119
  background: color-mix(in srgb, var(--accent-color) 10%, var(--background));
120
120
  color: var(--accent-color);
121
- animation: stepper-pulse 2.4s ease-out infinite;
121
+ animation: stepper-pulse 2.4s ease-out 3;
122
122
  }
123
123
 
124
124
  .stepper-title {