ngwr 12.1.0 → 13.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 (187) hide show
  1. package/README.md +11 -1
  2. package/alert/styles/_index.scss +11 -4
  3. package/badge/styles/_index.scss +30 -0
  4. package/badge/styles/_tag.scss +15 -1
  5. package/border-glow/border-glow.scss +1 -1
  6. package/border-glow/styles/_index.scss +15 -233
  7. package/border-glow/styles/_rules.scss +228 -0
  8. package/breadcrumbs/breadcrumbs.scss +1 -1
  9. package/breadcrumbs/styles/_index.scss +15 -78
  10. package/breadcrumbs/styles/_rules.scss +84 -0
  11. package/card/card.scss +1 -1
  12. package/card/styles/_index.scss +15 -96
  13. package/card/styles/_rules.scss +96 -0
  14. package/cascader/cascader.scss +1 -1
  15. package/cascader/styles/_index.scss +15 -244
  16. package/cascader/styles/_rules.scss +261 -0
  17. package/checkbox/styles/_index.scss +7 -0
  18. package/circular-text/circular-text.scss +1 -1
  19. package/circular-text/styles/_index.scss +15 -63
  20. package/circular-text/styles/_rules.scss +58 -0
  21. package/color-picker/styles/_index.scss +8 -0
  22. package/descriptions/styles/_index.scss +13 -3
  23. package/drag-drop/sortable-list.scss +1 -1
  24. package/drag-drop/styles/_index.scss +14 -143
  25. package/drag-drop/styles/_rules.scss +151 -0
  26. package/drawer/styles/_index.scss +95 -1
  27. package/falling-text/falling-text.scss +1 -1
  28. package/falling-text/styles/_index.scss +15 -36
  29. package/falling-text/styles/_rules.scss +31 -0
  30. package/fesm2022/ngwr-border-glow.mjs +2 -2
  31. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  32. package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
  33. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  34. package/fesm2022/ngwr-card.mjs +2 -2
  35. package/fesm2022/ngwr-card.mjs.map +1 -1
  36. package/fesm2022/ngwr-cascader.mjs +27 -9
  37. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  38. package/fesm2022/ngwr-checkbox.mjs +86 -8
  39. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  40. package/fesm2022/ngwr-circular-text.mjs +2 -2
  41. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  42. package/fesm2022/ngwr-color-picker.mjs +57 -7
  43. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  44. package/fesm2022/ngwr-date-picker.mjs +8 -0
  45. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  46. package/fesm2022/ngwr-drag-drop.mjs +2 -2
  47. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  48. package/fesm2022/ngwr-drawer.mjs +6 -0
  49. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  50. package/fesm2022/ngwr-falling-text.mjs +2 -2
  51. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  52. package/fesm2022/ngwr-file-upload.mjs +29 -8
  53. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  54. package/fesm2022/ngwr-form.mjs +48 -1
  55. package/fesm2022/ngwr-form.mjs.map +1 -1
  56. package/fesm2022/ngwr-glitch-text.mjs +2 -2
  57. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  58. package/fesm2022/ngwr-gradient-text.mjs +2 -2
  59. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  60. package/fesm2022/ngwr-input-otp.mjs +41 -3
  61. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  62. package/fesm2022/ngwr-knob.mjs +17 -2
  63. package/fesm2022/ngwr-knob.mjs.map +1 -1
  64. package/fesm2022/ngwr-lightbox.mjs +2 -2
  65. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  66. package/fesm2022/ngwr-list.mjs +2 -2
  67. package/fesm2022/ngwr-list.mjs.map +1 -1
  68. package/fesm2022/ngwr-loading-bar.mjs +2 -2
  69. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  70. package/fesm2022/ngwr-marquee.mjs +2 -2
  71. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  72. package/fesm2022/ngwr-pagination.mjs +1 -1
  73. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  74. package/fesm2022/ngwr-radio.mjs +63 -8
  75. package/fesm2022/ngwr-radio.mjs.map +1 -1
  76. package/fesm2022/ngwr-rating.mjs +17 -2
  77. package/fesm2022/ngwr-rating.mjs.map +1 -1
  78. package/fesm2022/ngwr-segmented.mjs +22 -3
  79. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  80. package/fesm2022/ngwr-select.mjs +59 -15
  81. package/fesm2022/ngwr-select.mjs.map +1 -1
  82. package/fesm2022/ngwr-shiny-text.mjs +2 -2
  83. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  84. package/fesm2022/ngwr-slider.mjs +21 -6
  85. package/fesm2022/ngwr-slider.mjs.map +1 -1
  86. package/fesm2022/ngwr-spotlight-card.mjs +2 -2
  87. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  88. package/fesm2022/ngwr-star-border.mjs +2 -2
  89. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  90. package/fesm2022/ngwr-switch.mjs +44 -4
  91. package/fesm2022/ngwr-switch.mjs.map +1 -1
  92. package/fesm2022/ngwr-table.mjs +1 -1
  93. package/fesm2022/ngwr-table.mjs.map +1 -1
  94. package/fesm2022/ngwr-textarea.mjs +5 -2
  95. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  96. package/fesm2022/ngwr-theme.mjs +41 -1
  97. package/fesm2022/ngwr-theme.mjs.map +1 -1
  98. package/fesm2022/ngwr-tilt-card.mjs +2 -2
  99. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  100. package/fesm2022/ngwr-transfer.mjs +38 -6
  101. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  102. package/fesm2022/ngwr-tree.mjs +33 -7
  103. package/fesm2022/ngwr-tree.mjs.map +1 -1
  104. package/fesm2022/ngwr-typewriter.mjs +2 -2
  105. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  106. package/fesm2022/ngwr-version.mjs +1 -1
  107. package/fesm2022/ngwr-version.mjs.map +1 -1
  108. package/fesm2022/ngwr-virtual-scroll.mjs +2 -2
  109. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  110. package/fesm2022/ngwr-waves.mjs +2 -2
  111. package/fesm2022/ngwr-waves.mjs.map +1 -1
  112. package/file-upload/styles/_index.scss +6 -0
  113. package/glitch-text/glitch-text.scss +1 -1
  114. package/glitch-text/styles/_index.scss +14 -164
  115. package/glitch-text/styles/_rules.scss +160 -0
  116. package/gradient-text/gradient-text.scss +1 -1
  117. package/gradient-text/styles/_index.scss +15 -118
  118. package/gradient-text/styles/_rules.scss +113 -0
  119. package/input/styles/_index.scss +13 -0
  120. package/input-otp/styles/_index.scss +8 -0
  121. package/lightbox/lightbox.scss +1 -1
  122. package/lightbox/styles/_index.scss +15 -191
  123. package/lightbox/styles/_rules.scss +198 -0
  124. package/list/list.scss +1 -1
  125. package/list/styles/_index.scss +15 -101
  126. package/list/styles/_rules.scss +108 -0
  127. package/llms-full.txt +3 -3
  128. package/loading-bar/loading-bar.scss +1 -1
  129. package/loading-bar/styles/_index.scss +15 -33
  130. package/loading-bar/styles/_rules.scss +28 -0
  131. package/marquee/marquee.scss +1 -1
  132. package/marquee/styles/_index.scss +15 -155
  133. package/marquee/styles/_rules.scss +150 -0
  134. package/overlay/styles/_arrow.scss +243 -0
  135. package/package.json +1 -1
  136. package/popconfirm/styles/_index.scss +17 -0
  137. package/popover/styles/_index.scss +49 -207
  138. package/segmented/styles/_index.scss +8 -0
  139. package/select/styles/_index.scss +47 -5
  140. package/shiny-text/shiny-text.scss +1 -1
  141. package/shiny-text/styles/_index.scss +14 -105
  142. package/shiny-text/styles/_rules.scss +101 -0
  143. package/sidebar/styles/_index.scss +4 -1
  144. package/slider/styles/_index.scss +8 -0
  145. package/spotlight-card/spotlight-card.scss +1 -1
  146. package/spotlight-card/styles/_index.scss +15 -52
  147. package/spotlight-card/styles/_rules.scss +47 -0
  148. package/star-border/star-border.scss +1 -1
  149. package/star-border/styles/_index.scss +15 -125
  150. package/star-border/styles/_rules.scss +120 -0
  151. package/stepper/styles/_index.scss +44 -5
  152. package/switch/styles/_index.scss +7 -0
  153. package/theme/styles/_dark.scss +81 -4
  154. package/theme/styles/_index.scss +1 -0
  155. package/theme/styles/_mixins.scss +10 -0
  156. package/theme/styles/_motion.scss +94 -0
  157. package/tilt-card/styles/_index.scss +15 -45
  158. package/tilt-card/styles/_rules.scss +40 -0
  159. package/tilt-card/tilt-card.scss +1 -1
  160. package/tree/styles/_index.scss +25 -3
  161. package/types/ngwr-cascader.d.ts +17 -1
  162. package/types/ngwr-checkbox.d.ts +51 -3
  163. package/types/ngwr-color-picker.d.ts +19 -1
  164. package/types/ngwr-file-upload.d.ts +18 -1
  165. package/types/ngwr-form.d.ts +51 -2
  166. package/types/ngwr-input-otp.d.ts +23 -1
  167. package/types/ngwr-knob.d.ts +14 -0
  168. package/types/ngwr-radio.d.ts +37 -2
  169. package/types/ngwr-rating.d.ts +14 -0
  170. package/types/ngwr-segmented.d.ts +18 -1
  171. package/types/ngwr-select.d.ts +29 -3
  172. package/types/ngwr-slider.d.ts +14 -1
  173. package/types/ngwr-switch.d.ts +26 -2
  174. package/types/ngwr-textarea.d.ts +3 -0
  175. package/types/ngwr-theme.d.ts +24 -0
  176. package/types/ngwr-transfer.d.ts +17 -1
  177. package/types/ngwr-tree.d.ts +21 -1
  178. package/types/ngwr-version.d.ts +1 -1
  179. package/typewriter/styles/_index.scss +15 -38
  180. package/typewriter/styles/_rules.scss +33 -0
  181. package/typewriter/typewriter.scss +1 -1
  182. package/virtual-scroll/styles/_index.scss +15 -32
  183. package/virtual-scroll/styles/_rules.scss +27 -0
  184. package/virtual-scroll/virtual-scroll.scss +1 -1
  185. package/waves/styles/_index.scss +15 -50
  186. package/waves/styles/_rules.scss +45 -0
  187. package/waves/waves.scss +1 -1
@@ -1,52 +1,15 @@
1
- // Pull the whole theme so a standalone `@use 'ngwr/<name>'` brings in every
2
- // design token this stylesheet references. Sass deduplicates `@use` per
3
- // compilation, so the umbrella sheet still emits the theme only once.
4
- @use '../../theme/styles' as theme;
5
-
6
- // Cursor-tracked spotlight card (reactbits port). The `--wr-spotlight-x/y`
7
- // vars come from the host's mousemove handler; `--wr-spotlight-color` is
8
- // set inline from the `[spotlightColor]` input when provided — otherwise
9
- // the theme picks the default below. `--wr-color-on-surface-rgb` flips between
10
- // themes (dark on light surfaces, light on dark), so a single tint reads
11
- // as a dark-ish glow in light mode and a light beam in dark mode.
12
-
13
- .wr-spotlight-card {
14
- --wr-spotlight-x: 50%;
15
- --wr-spotlight-y: 50%;
16
- --wr-spotlight-color: rgba(var(--wr-color-on-surface-rgb), 0.15);
17
-
18
- position: relative;
19
- display: block;
20
- border-radius: 1.5rem;
21
- border: 1px solid var(--wr-color-outline);
22
- background-color: var(--wr-color-surface);
23
- padding: 2rem;
24
- overflow: hidden;
25
- color: var(--wr-color-on-surface);
26
-
27
- &::before {
28
- content: '';
29
- position: absolute;
30
- inset: 0;
31
- background: radial-gradient(
32
- circle at var(--wr-spotlight-x) var(--wr-spotlight-y),
33
- var(--wr-spotlight-color),
34
- transparent var(--wr-spotlight-radius, 80%)
35
- );
36
- opacity: 0;
37
- transition: opacity 0.5s ease;
38
- pointer-events: none;
39
- }
40
-
41
- &:hover::before,
42
- &:focus-within::before {
43
- opacity: 0.6;
44
- }
45
- }
46
-
47
- // Dark mode: `--wr-color-on-surface-rgb` already flips to a light value, so the
48
- // default tint above becomes a light beam on the navy surface — nudge the
49
- // alpha up a touch so it reads as bright as the classic spotlight.
50
- [data-theme='dark'] .wr-spotlight-card {
51
- --wr-spotlight-color: rgba(var(--wr-color-on-surface-rgb), 0.22);
52
- }
1
+ // Public Sass entry for `@use 'ngwr/spotlight-card'`.
2
+ //
3
+ // Pulls the whole theme so a standalone import still brings in every design
4
+ // token these rules reference, then loads the rules themselves from `_rules`.
5
+ //
6
+ // The split is what keeps the token layer out of the JavaScript bundle.
7
+ // `spotlight-card.scss` is the component's `styleUrl`, and a component stylesheet is
8
+ // its OWN Sass compilation — `@use` only deduplicates within one. So while the
9
+ // umbrella sheet emits the theme once, forwarding it from here would inline a
10
+ // second, byte-identical copy of `:root`, `[data-theme='dark']` and the
11
+ // box-sizing reset into the compiled component styles, which ng-packagr then
12
+ // embeds in the FESM bundle as a string. `spotlight-card.scss` forwards `_rules`
13
+ // instead, so it carries this component's own CSS and nothing else.
14
+ @use '../../theme/styles';
15
+ @forward 'rules';
@@ -0,0 +1,47 @@
1
+ // Cursor-tracked spotlight card (reactbits port). The `--wr-spotlight-x/y`
2
+ // vars come from the host's mousemove handler; `--wr-spotlight-color` is
3
+ // set inline from the `[spotlightColor]` input when provided — otherwise
4
+ // the theme picks the default below. `--wr-color-on-surface-rgb` flips between
5
+ // themes (dark on light surfaces, light on dark), so a single tint reads
6
+ // as a dark-ish glow in light mode and a light beam in dark mode.
7
+
8
+ .wr-spotlight-card {
9
+ --wr-spotlight-x: 50%;
10
+ --wr-spotlight-y: 50%;
11
+ --wr-spotlight-color: rgba(var(--wr-color-on-surface-rgb), 0.15);
12
+
13
+ position: relative;
14
+ display: block;
15
+ border-radius: 1.5rem;
16
+ border: 1px solid var(--wr-color-outline);
17
+ background-color: var(--wr-color-surface);
18
+ padding: 2rem;
19
+ overflow: hidden;
20
+ color: var(--wr-color-on-surface);
21
+
22
+ &::before {
23
+ content: '';
24
+ position: absolute;
25
+ inset: 0;
26
+ background: radial-gradient(
27
+ circle at var(--wr-spotlight-x) var(--wr-spotlight-y),
28
+ var(--wr-spotlight-color),
29
+ transparent var(--wr-spotlight-radius, 80%)
30
+ );
31
+ opacity: 0;
32
+ transition: opacity 0.5s ease;
33
+ pointer-events: none;
34
+ }
35
+
36
+ &:hover::before,
37
+ &:focus-within::before {
38
+ opacity: 0.6;
39
+ }
40
+ }
41
+
42
+ // Dark mode: `--wr-color-on-surface-rgb` already flips to a light value, so the
43
+ // default tint above becomes a light beam on the navy surface — nudge the
44
+ // alpha up a touch so it reads as bright as the classic spotlight.
45
+ [data-theme='dark'] .wr-spotlight-card {
46
+ --wr-spotlight-color: rgba(var(--wr-color-on-surface-rgb), 0.22);
47
+ }
@@ -1 +1 @@
1
- @forward './styles';
1
+ @forward './styles/rules';
@@ -1,125 +1,15 @@
1
- // Pull the whole theme so a standalone `@use 'ngwr/<name>'` brings in every
2
- // design token this stylesheet references. Sass deduplicates `@use` per
3
- // compilation, so the umbrella sheet still emits the theme only once.
4
- @use '../../theme/styles' as theme;
5
-
6
- // Star border (reactbits port). Two oversized radial-gradient "comets"
7
- // sweep along the top and bottom edges of the rounded container; the
8
- // projected content sits on a token-themed inner panel above them.
9
-
10
- .wr-star-border {
11
- // Light surfaces get a primary comet; dark theme flips to the classic
12
- // white (see block below). `[color]` overrides both inline. The light
13
- // comet gets a wider core + full opacity — a thin 10% tail washes out
14
- // to gray on white surfaces.
15
- --wr-star-border-color: var(--wr-color-primary);
16
- --wr-star-border-speed: 6s;
17
- --wr-star-border-radius: 20px;
18
- --wr-star-border-stop: 16%;
19
- --wr-star-border-opacity: 1;
20
-
21
- display: inline-block;
22
- position: relative;
23
- border: 0;
24
- background: transparent;
25
- border-radius: var(--wr-star-border-radius);
26
- overflow: hidden;
27
- cursor: inherit;
28
-
29
- &__ray {
30
- position: absolute;
31
- display: block;
32
- width: 300%;
33
- height: 50%;
34
- opacity: var(--wr-star-border-opacity);
35
- border-radius: 50%;
36
- background: radial-gradient(circle, var(--wr-star-border-color), transparent var(--wr-star-border-stop));
37
- animation: wr-star-border-bottom var(--wr-star-border-speed) linear infinite alternate;
38
- z-index: 0;
39
-
40
- &--bottom {
41
- bottom: -12px;
42
- // rtl-ok: the comet's off-screen start mark — `wr-star-border-bottom` sweeps it by `translate(-100%, 0%)`
43
- right: -250%;
44
- }
45
-
46
- &--top {
47
- top: -12px;
48
- // rtl-ok: the comet's off-screen start mark — `wr-star-border-top` sweeps it by `translate(100%, 0%)`
49
- left: -250%;
50
- animation-name: wr-star-border-top;
51
- }
52
- }
53
-
54
- // Single-ray variant — drop the top comet.
55
- &--single &__ray--top {
56
- display: none;
57
- }
58
-
59
- // Hover-only variant — rays sit hidden until the pointer is in. The
60
- // idle state must drop the animation entirely (not pause it): a paused
61
- // animation pins `opacity` at its 0% keyframe value, which beats the
62
- // cascade and leaves a frozen comet visible.
63
- &--hover &__ray {
64
- opacity: 0;
65
- animation: none;
66
- }
67
-
68
- &--hover:hover &__ray {
69
- animation: wr-star-border-bottom var(--wr-star-border-speed) linear infinite alternate;
70
- }
71
-
72
- &--hover:hover &__ray--top {
73
- animation-name: wr-star-border-top;
74
- }
75
-
76
- &__inner {
77
- position: relative;
78
- display: block;
79
- border: 1px solid var(--wr-color-outline);
80
- background: var(--wr-color-surface);
81
- color: var(--wr-color-on-surface);
82
- font-size: 1rem;
83
- text-align: center;
84
- padding: 1rem 1.625rem;
85
- border-radius: var(--wr-star-border-radius);
86
- z-index: 1;
87
- }
88
- }
89
-
90
- [data-theme='dark'] .wr-star-border {
91
- --wr-star-border-color: #ffffff;
92
- --wr-star-border-stop: 10%;
93
- --wr-star-border-opacity: 0.7;
94
- }
95
-
96
- @keyframes wr-star-border-bottom {
97
- 0% {
98
- transform: translate(0%, 0%);
99
- opacity: 1;
100
- }
101
- 100% {
102
- transform: translate(-100%, 0%);
103
- opacity: 0;
104
- }
105
- }
106
-
107
- @keyframes wr-star-border-top {
108
- 0% {
109
- transform: translate(0%, 0%);
110
- opacity: 1;
111
- }
112
- 100% {
113
- transform: translate(100%, 0%);
114
- opacity: 0;
115
- }
116
- }
117
-
118
- // Reduced motion: no sweeping comets — a frozen ray reads as a smudge,
119
- // so hide them and keep the panel border as the resting look.
120
- @media (prefers-reduced-motion: reduce) {
121
- .wr-star-border__ray {
122
- animation: none !important;
123
- opacity: 0 !important;
124
- }
125
- }
1
+ // Public Sass entry for `@use 'ngwr/star-border'`.
2
+ //
3
+ // Pulls the whole theme so a standalone import still brings in every design
4
+ // token these rules reference, then loads the rules themselves from `_rules`.
5
+ //
6
+ // The split is what keeps the token layer out of the JavaScript bundle.
7
+ // `star-border.scss` is the component's `styleUrl`, and a component stylesheet is
8
+ // its OWN Sass compilation — `@use` only deduplicates within one. So while the
9
+ // umbrella sheet emits the theme once, forwarding it from here would inline a
10
+ // second, byte-identical copy of `:root`, `[data-theme='dark']` and the
11
+ // box-sizing reset into the compiled component styles, which ng-packagr then
12
+ // embeds in the FESM bundle as a string. `star-border.scss` forwards `_rules`
13
+ // instead, so it carries this component's own CSS and nothing else.
14
+ @use '../../theme/styles';
15
+ @forward 'rules';
@@ -0,0 +1,120 @@
1
+ // Star border (reactbits port). Two oversized radial-gradient "comets"
2
+ // sweep along the top and bottom edges of the rounded container; the
3
+ // projected content sits on a token-themed inner panel above them.
4
+
5
+ .wr-star-border {
6
+ // Light surfaces get a primary comet; dark theme flips to the classic
7
+ // white (see block below). `[color]` overrides both inline. The light
8
+ // comet gets a wider core + full opacity — a thin 10% tail washes out
9
+ // to gray on white surfaces.
10
+ --wr-star-border-color: var(--wr-color-primary);
11
+ --wr-star-border-speed: 6s;
12
+ --wr-star-border-radius: 20px;
13
+ --wr-star-border-stop: 16%;
14
+ --wr-star-border-opacity: 1;
15
+
16
+ display: inline-block;
17
+ position: relative;
18
+ border: 0;
19
+ background: transparent;
20
+ border-radius: var(--wr-star-border-radius);
21
+ overflow: hidden;
22
+ cursor: inherit;
23
+
24
+ &__ray {
25
+ position: absolute;
26
+ display: block;
27
+ width: 300%;
28
+ height: 50%;
29
+ opacity: var(--wr-star-border-opacity);
30
+ border-radius: 50%;
31
+ background: radial-gradient(circle, var(--wr-star-border-color), transparent var(--wr-star-border-stop));
32
+ animation: wr-star-border-bottom var(--wr-star-border-speed) linear infinite alternate;
33
+ z-index: 0;
34
+
35
+ &--bottom {
36
+ bottom: -12px;
37
+ // rtl-ok: the comet's off-screen start mark — `wr-star-border-bottom` sweeps it by `translate(-100%, 0%)`
38
+ right: -250%;
39
+ }
40
+
41
+ &--top {
42
+ top: -12px;
43
+ // rtl-ok: the comet's off-screen start mark — `wr-star-border-top` sweeps it by `translate(100%, 0%)`
44
+ left: -250%;
45
+ animation-name: wr-star-border-top;
46
+ }
47
+ }
48
+
49
+ // Single-ray variant — drop the top comet.
50
+ &--single &__ray--top {
51
+ display: none;
52
+ }
53
+
54
+ // Hover-only variant — rays sit hidden until the pointer is in. The
55
+ // idle state must drop the animation entirely (not pause it): a paused
56
+ // animation pins `opacity` at its 0% keyframe value, which beats the
57
+ // cascade and leaves a frozen comet visible.
58
+ &--hover &__ray {
59
+ opacity: 0;
60
+ animation: none;
61
+ }
62
+
63
+ &--hover:hover &__ray {
64
+ animation: wr-star-border-bottom var(--wr-star-border-speed) linear infinite alternate;
65
+ }
66
+
67
+ &--hover:hover &__ray--top {
68
+ animation-name: wr-star-border-top;
69
+ }
70
+
71
+ &__inner {
72
+ position: relative;
73
+ display: block;
74
+ border: 1px solid var(--wr-color-outline);
75
+ background: var(--wr-color-surface);
76
+ color: var(--wr-color-on-surface);
77
+ font-size: 1rem;
78
+ text-align: center;
79
+ padding: 1rem 1.625rem;
80
+ border-radius: var(--wr-star-border-radius);
81
+ z-index: 1;
82
+ }
83
+ }
84
+
85
+ [data-theme='dark'] .wr-star-border {
86
+ --wr-star-border-color: #ffffff;
87
+ --wr-star-border-stop: 10%;
88
+ --wr-star-border-opacity: 0.7;
89
+ }
90
+
91
+ @keyframes wr-star-border-bottom {
92
+ 0% {
93
+ transform: translate(0%, 0%);
94
+ opacity: 1;
95
+ }
96
+ 100% {
97
+ transform: translate(-100%, 0%);
98
+ opacity: 0;
99
+ }
100
+ }
101
+
102
+ @keyframes wr-star-border-top {
103
+ 0% {
104
+ transform: translate(0%, 0%);
105
+ opacity: 1;
106
+ }
107
+ 100% {
108
+ transform: translate(100%, 0%);
109
+ opacity: 0;
110
+ }
111
+ }
112
+
113
+ // Reduced motion: no sweeping comets — a frozen ray reads as a smudge,
114
+ // so hide them and keep the panel border as the resting look.
115
+ @media (prefers-reduced-motion: reduce) {
116
+ .wr-star-border__ray {
117
+ animation: none !important;
118
+ opacity: 0 !important;
119
+ }
120
+ }
@@ -1,5 +1,13 @@
1
1
  @use '../../theme/styles' as theme;
2
2
 
3
+ // The label's own line box, and the header button's block padding. Both are
4
+ // declared once below and read back by the alignment arithmetic — the indicator
5
+ // centres on the label's FIRST LINE, and the connector centres on the indicator,
6
+ // so a literal repeated in three places is a defect waiting for someone to
7
+ // change one of them.
8
+ $label-line-height: 1.25rem;
9
+ $header-padding-block: 0.25rem;
10
+
3
11
  .wr-stepper {
4
12
  --wr-stepper-indicator-size: 1.75rem;
5
13
  --wr-stepper-connector: var(--wr-color-outline);
@@ -33,9 +41,19 @@
33
41
  flex-direction: column;
34
42
  }
35
43
 
44
+ // `flex-start`, not `center`: a step that carries a hint line
45
+ // (`description` / `optional`) is TALLER than its neighbours, and centring
46
+ // every button in the row it defines drops the short steps' circles by half
47
+ // the difference — the whole row reads as misaligned because of one step.
48
+ // Anchored at the block start, every button's first line sits at the same y,
49
+ // and the connector places itself against the indicator (see `&__connector`)
50
+ // instead of riding the row's centre.
51
+ //
52
+ // A row of same-height steps is unaffected: with equal heights `center` and
53
+ // `flex-start` resolve identically.
36
54
  &__header {
37
55
  display: flex;
38
- align-items: center;
56
+ align-items: flex-start;
39
57
  flex: 1 1 auto;
40
58
  gap: 0.75rem;
41
59
  min-width: 0;
@@ -59,13 +77,20 @@
59
77
  flex: 0 0 auto;
60
78
  }
61
79
 
80
+ // `flex-start` here for the same reason, one level in: the indicator is a
81
+ // fixed-size circle and the label is a column that grows a line whenever the
82
+ // step has a `description` or is `optional`. Centred against the whole block,
83
+ // the circle slides down as the label grows and stops belonging to the step's
84
+ // NAME. The label carries the compensating offset (see `&__label`) rather
85
+ // than the indicator carrying a negative one, so the circle stays inside the
86
+ // button's own box.
62
87
  &__header-button {
63
88
  appearance: none;
64
89
  background: transparent;
65
90
  border: 0;
66
- padding: 0.25rem 0;
91
+ padding: $header-padding-block 0;
67
92
  display: inline-flex;
68
- align-items: center;
93
+ align-items: flex-start;
69
94
  gap: 0.625rem;
70
95
  color: var(--wr-color-on-surface);
71
96
  cursor: pointer;
@@ -119,7 +144,15 @@
119
144
  gap: 0.0625rem;
120
145
  font-size: var(--wr-text-sm);
121
146
  font-weight: 500;
122
- line-height: 1.25rem;
147
+ line-height: $label-line-height;
148
+ // Centre the label's FIRST LINE on the indicator, now that both are anchored
149
+ // to the button's block start. Half the difference between the circle and one
150
+ // line box — derived, not typed, because `--wr-stepper-indicator-size` is a
151
+ // public custom property a consumer can retune. At the shipped 1.75rem this
152
+ // is 0.25rem, which is exactly where `align-items: center` used to put a
153
+ // single-line label: the one-line step renders pixel-for-pixel as before and
154
+ // only the multi-line one moves.
155
+ margin-block-start: calc((var(--wr-stepper-indicator-size) - #{$label-line-height}) / 2);
123
156
  }
124
157
 
125
158
  &__header--active &__label {
@@ -145,7 +178,13 @@
145
178
  height: 1.5px;
146
179
  background: var(--wr-stepper-connector);
147
180
  min-width: 1rem;
148
- margin: 0 0.5rem;
181
+ // The row aligns its children to the block start now, so the line has to
182
+ // place itself on the indicator's centre instead of inheriting the row's.
183
+ // Button padding, plus half a circle, less half the line's own thickness.
184
+ // Same y the centred version produced for a row of equal-height steps — and
185
+ // unlike it, the same y when one step grows a hint line.
186
+ margin-block-start: calc(#{$header-padding-block} + var(--wr-stepper-indicator-size) / 2 - 0.75px);
187
+ margin-inline: 0.5rem;
149
188
  }
150
189
 
151
190
  &--vertical &__connector {
@@ -95,6 +95,13 @@
95
95
  }
96
96
  }
97
97
 
98
+ // A read-only switch still takes focus and still submits its value, so it
99
+ // keeps the full visual and only stops inviting the click — dimming it would
100
+ // say "disabled", which is the other state.
101
+ &--readonly &__label {
102
+ cursor: default;
103
+ }
104
+
98
105
  &--disabled {
99
106
  --wr-switch-color: var(--wr-color-on-surface-muted);
100
107
  pointer-events: none;
@@ -3,13 +3,90 @@
3
3
  @use 'colors';
4
4
  @use 'variables';
5
5
 
6
- // Dark theme overrides — opt-in via `[data-theme="dark"]` on <html>.
7
- // Drive the toggle from `WrTheme` (provided automatically with
8
- // `provideWrTheme(...)`).
6
+ // Dark theme overrides — opt-in via `[data-theme="dark"]` on <html>, or
7
+ // whatever `$theme-attribute` below is configured to. Drive the toggle from
8
+ // `WrTheme` (provided automatically with `provideWrTheme(...)`).
9
9
  //
10
10
  // Only semantic tokens flip — palette hues stay so brand-coloured
11
11
  // elements look the same regardless of theme.
12
12
 
13
+ /// The attribute the dark block keys on. Must match the `attribute` passed to
14
+ /// `provideWrTheme(...)`, and that pairing is the whole reason this variable
15
+ /// exists: `WrThemeConfig.attribute` is a RUNTIME value, and no CSS selector can
16
+ /// read one. Until v13 the selector below was the literal `[data-theme='dark']`,
17
+ /// so an app that set `provideWrTheme({ attribute: 'data-color-mode' })` got a
18
+ /// service reporting `resolved() === 'dark'`, the attribute written correctly
19
+ /// onto `<html>`, and a page that stayed light forever — no error, no warning.
20
+ ///
21
+ /// Configure the two together:
22
+ ///
23
+ /// ```scss
24
+ /// // styles.scss
25
+ /// @use 'ngwr' with ($theme-attribute: 'data-color-mode');
26
+ /// ```
27
+ /// ```ts
28
+ /// provideWrTheme({ attribute: 'data-color-mode' })
29
+ /// ```
30
+ ///
31
+ /// Only the configured attribute is emitted — the default is NOT kept as a
32
+ /// second selector. Renaming it is what decouples ngwr from another design
33
+ /// system that already owns `data-theme`, and emitting both would re-couple
34
+ /// them: that system's own `data-theme="dark"` would flip ngwr's tokens behind
35
+ /// the app's back, which is the exact collision the rename exists to avoid.
36
+ ///
37
+ /// If the two disagree the page silently stays light, so `WrTheme` publishes a
38
+ /// dev-mode check for it: the light block below writes the configured name into
39
+ /// `--wr-theme-attribute`, and the service warns when what it reads there is not
40
+ /// what it was configured with. A stylesheet that is missing or predates v13
41
+ /// leaves the property empty and the check stays quiet — it can only fire on a
42
+ /// real disagreement.
43
+ $theme-attribute: 'data-theme' !default;
44
+
45
+ /// The dark-theme selector, for a stylesheet that has to build its own.
46
+ ///
47
+ /// @example scss
48
+ /// #{dark.dark-selector()} .my-thing { ... }
49
+ @function dark-selector() {
50
+ @return '[#{$theme-attribute}=\'dark\']';
51
+ }
52
+
53
+ /// Wrap a declaration block so it only applies in dark mode, honouring
54
+ /// `$theme-attribute`. Prefer this over writing `[data-theme='dark']` by hand —
55
+ /// a hand-written literal is exactly what BUG-3 was.
56
+ ///
57
+ /// @example scss
58
+ /// @use 'ngwr/theme' as theme;
59
+ ///
60
+ /// .my-card {
61
+ /// background: #fff;
62
+ ///
63
+ /// @include theme.dark {
64
+ /// background: #0b1120;
65
+ /// }
66
+ /// }
67
+ @mixin dark() {
68
+ @if & {
69
+ @at-root #{dark-selector()} #{&} {
70
+ @content;
71
+ }
72
+ } @else {
73
+ #{dark-selector()} {
74
+ @content;
75
+ }
76
+ }
77
+ }
78
+
79
+ :root {
80
+ // The attribute this compiled stylesheet keys its dark block on, published so
81
+ // `WrTheme` can compare it against `WrThemeConfig.attribute` in dev mode and
82
+ // say so when the two were configured apart. Read with
83
+ // `getPropertyValue('--wr-theme-attribute')`, never with `var()`.
84
+ //
85
+ // unused-ok: read by `WrTheme` through the CSSOM rather than painted with —
86
+ // it carries a name, not a value, so no declaration can reference it.
87
+ --wr-theme-attribute: #{$theme-attribute};
88
+ }
89
+
13
90
  // Intents re-tuned for the dark canvas. Looped (below) so each gets the
14
91
  // FULL symmetric shade set — the hand blocks used to ship only base+rgb,
15
92
  // leaving -light/-dark/-lighter/-darker baked from the LIGHT palette.
@@ -37,7 +114,7 @@ $_light: #262f44;
37
114
  $_medium: #6d7682;
38
115
  $_dark: #e6ebf3;
39
116
 
40
- [data-theme='dark'] {
117
+ #{dark-selector()} {
41
118
  // Canvas (was `--wr-color-white` in light mode)
42
119
  // Deeper than slate-900 so an elevated surface in slate-800 reads
43
120
  // as a real layer rather than vanishing into the background.
@@ -4,3 +4,4 @@
4
4
  @forward 'dark';
5
5
  @forward 'focus';
6
6
  @forward 'mixins';
7
+ @forward 'motion';
@@ -43,6 +43,16 @@
43
43
  /// }
44
44
  /// ```
45
45
  ///
46
+ /// The reset has to sit on the element the mixin ran on. `corner-shape`
47
+ /// does not inherit, so a component whose modifier is on the HOST while
48
+ /// `smooth-br` is on a child needs to target that child — otherwise the
49
+ /// declaration lands on an element that never had a squircle to undo,
50
+ /// and, since `round` is the property's initial value, changes nothing
51
+ /// at all. `wr-select` shipped that way: `.wr-select--rounded` set it on
52
+ /// the host, the trigger kept `squircle` at the pill radius, and the
53
+ /// control painted a near-rectangle — 2px of corner where a stadium
54
+ /// wants 18.
55
+ ///
46
56
  /// @example scss
47
57
  /// .thing {
48
58
  /// @include smooth-br(0.625rem);