@kubex/zinc 1.1.98 → 1.1.100

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 (66) hide show
  1. package/AGENTS.md +5 -0
  2. package/CLAUDE.md +5 -0
  3. package/dist/custom-elements.json +876 -18
  4. package/dist/vscode.html-custom-data.json +148 -9
  5. package/dist/web-types.json +306 -12
  6. package/dist/zn.d.ts +203 -9
  7. package/dist/zn.min.css +1 -1
  8. package/dist/zn.min.js +343 -297
  9. package/docs/pages/components/background.md +143 -0
  10. package/docs/pages/components/checkbox-group.md +13 -1
  11. package/docs/pages/components/checkbox.md +59 -1
  12. package/docs/pages/components/radio-group.md +12 -0
  13. package/docs/pages/components/radio.md +188 -0
  14. package/docs/pages/components/remarkd-editor.md +22 -0
  15. package/docs/pages/components/scroll-container.md +14 -15
  16. package/package.json +1 -1
  17. package/scss/_root.scss +16 -0
  18. package/scss/mixins/_typography-mixins.scss +1 -0
  19. package/scss/shared/_button.scss +4 -2
  20. package/scss/themes/_typography.scss +2 -0
  21. package/src/components/background/background.component.ts +111 -0
  22. package/src/components/background/background.scss +236 -0
  23. package/src/components/background/background.test.ts +120 -0
  24. package/src/components/background/index.ts +12 -0
  25. package/src/components/button/button.scss +14 -4
  26. package/src/components/checkbox/checkbox.component.ts +62 -6
  27. package/src/components/checkbox/checkbox.scss +1 -0
  28. package/src/components/checkbox/checkbox.test.ts +56 -0
  29. package/src/components/checkbox-group/checkbox-group.component.ts +15 -0
  30. package/src/components/checkbox-group/checkbox-group.scss +23 -5
  31. package/src/components/checkbox-group/checkbox-group.test.ts +15 -0
  32. package/src/components/confirm/confirm.component.ts +3 -3
  33. package/src/components/confirm/confirm.test.ts +15 -0
  34. package/src/components/dialog/dialog.component.ts +3 -2
  35. package/src/components/editor/editor.component.ts +7 -6
  36. package/src/components/header/header.scss +3 -1
  37. package/src/components/menu-item/menu-item.scss +8 -5
  38. package/src/components/navbar/navbar.scss +11 -0
  39. package/src/components/page/page.scss +16 -2
  40. package/src/components/radio/radio.component.ts +63 -7
  41. package/src/components/radio/radio.scss +1 -0
  42. package/src/components/radio/radio.test.ts +56 -0
  43. package/src/components/radio-group/radio-group.component.ts +16 -3
  44. package/src/components/radio-group/radio-group.scss +23 -4
  45. package/src/components/radio-group/radio-group.test.ts +15 -0
  46. package/src/components/remarkd-editor/remarkd-editor.component.ts +118 -5
  47. package/src/components/remarkd-editor/remarkd-editor.scss +8 -1
  48. package/src/components/remarkd-editor/remarkd-editor.test.ts +96 -0
  49. package/src/components/scroll-container/scroll-container.component.ts +15 -1
  50. package/src/components/scroll-container/scroll-container.scss +4 -2
  51. package/src/components/scroll-container/scroll-container.test.ts +13 -0
  52. package/src/components/slideout/slideout.component.ts +3 -2
  53. package/src/components/thumbnail-group/thumbnail-group.scss +0 -1
  54. package/src/components/tile/tile.scss +2 -2
  55. package/src/components/toggle/toggle.scss +8 -1
  56. package/src/components/tooltip/tooltip.scss +4 -1
  57. package/src/components/translation-group/translation-group.test.ts +22 -0
  58. package/src/components/translations/translations.component.ts +18 -3
  59. package/src/form-control.scss +3 -1
  60. package/src/internal/conditional.ts +2 -2
  61. package/src/internal/form.ts +2 -1
  62. package/src/internal/selection-card.ts +19 -0
  63. package/src/selection-card.scss +187 -0
  64. package/src/utilities/query.test.ts +18 -1
  65. package/src/utilities/query.ts +8 -0
  66. package/src/zinc.ts +1 -0
@@ -2,7 +2,9 @@
2
2
  @use '../variables';
3
3
 
4
4
  @mixin button {
5
- border-radius: var(--zn-border-radius);
5
+ // Buttons take their own radius token so a theme can make controls pills
6
+ // without reshaping panels, inputs and dialogs at the same time.
7
+ border-radius: var(--zn-button-border-radius, var(--zn-border-radius));
6
8
  display: inline-flex;
7
9
  align-items: center;
8
10
  justify-content: center;
@@ -10,7 +12,7 @@
10
12
  margin: auto;
11
13
  width: fit-content;
12
14
  background-color: rgb(var(--zn-primary));
13
- color: rgba(255, 255, 255, 0.9);
15
+ color: rgba(var(--zn-color-on-primary, 255, 255, 255), 0.9);
14
16
  font-size: var(--zn-text-button-label-font-size);
15
17
  font-weight: var(--zn-text-button-label-font-weight);
16
18
  line-height: var(--zn-text-button-label-line-height);
@@ -12,6 +12,7 @@
12
12
  * --zn-text-<role>-font-weight
13
13
  * --zn-text-<role>-color
14
14
  * --zn-text-<role>-transform
15
+ * --zn-text-<role>-letter-spacing
15
16
  *
16
17
  * All roles use the sans family (--zn-font-sans). Apply a role
17
18
  * in a component with the text-style() mixin from scss/mixins.
@@ -59,5 +60,6 @@ $text-styles: (
59
60
  --zn-text-#{$role}-color: var(--zn-text-color);
60
61
  }
61
62
  --zn-text-#{$role}-transform: #{map.get($style, "transform")};
63
+ --zn-text-#{$role}-letter-spacing: var(--zn-letter-spacing-normal, normal);
62
64
  }
63
65
  }
@@ -0,0 +1,111 @@
1
+ import {type CSSResultGroup, html, unsafeCSS} from 'lit';
2
+ import {property} from 'lit/decorators.js';
3
+ import {ResizeController} from '@lit-labs/observers/resize-controller.js';
4
+ import {styleMap} from 'lit/directives/style-map.js';
5
+ import ZincElement from '../../internal/zinc-element';
6
+ import ZnIcon from '../icon';
7
+
8
+ import styles from './background.scss';
9
+
10
+ export type BackgroundImageStrength = 'soft' | 'medium' | 'full';
11
+ export type BackgroundMotion = 'none' | 'drift' | 'breathe';
12
+ export type BackgroundOverlay = 'none' | 'soft' | 'strong';
13
+ export type BackgroundOverlayTone = 'light' | 'dark';
14
+
15
+ /**
16
+ * @summary Composes a colour, decorative image, image strength, optional motion and contrast overlay behind slotted content.
17
+ *
18
+ * @documentation https://zinc.style/components/background
19
+ * @status experimental
20
+ * @since 1.0
21
+ *
22
+ * @dependency zn-icon
23
+ *
24
+ * @slot - Content displayed above the background layers.
25
+ *
26
+ * @csspart base - The component's full-size background canvas.
27
+ * @csspart image - The decorative background image.
28
+ * @csspart overlay - The contrast overlay between the image and content.
29
+ * @csspart floating-icons - The non-interactive layer containing the floating icons.
30
+ * @csspart floating-icon - Each floating `zn-icon`.
31
+ * @csspart content - The wrapper around the default slot.
32
+ *
33
+ * @cssproperty --zn-background-color - Fallback colour when the `color` attribute is not set.
34
+ * @cssproperty --zn-background-image-position - Position of the background image. Defaults to `center`.
35
+ * @cssproperty --zn-background-overlay-angle - Direction of the overlay gradient. Defaults to `110deg`.
36
+ * @cssproperty --zn-background-floating-icon-color - Colour of the floating icons. Defaults to `currentColor`.
37
+ */
38
+ export default class ZnBackground extends ZincElement {
39
+ static styles: CSSResultGroup = unsafeCSS(styles);
40
+ static dependencies = {'zn-icon': ZnIcon};
41
+
42
+ private readonly resizeObserver = new ResizeController<number>(this, {
43
+ callback: entries => {
44
+ const width = entries[0]?.contentRect.width ?? this.offsetWidth;
45
+ return Math.max(1, Math.min(width * 0.05, 150));
46
+ },
47
+ });
48
+
49
+ /** URL of the decorative background image. */
50
+ @property() image = '';
51
+
52
+ /** CSS colour painted beneath the image. */
53
+ @property() color = '';
54
+
55
+ /** Visibility of the image: `soft` (30%), `medium` (62%) or `full` (100%). */
56
+ @property({attribute: 'image-strength', reflect: true}) imageStrength: BackgroundImageStrength = 'full';
57
+
58
+ /** Subtle animation applied to the image. Motion stops when reduced motion is requested. */
59
+ @property({reflect: true}) motion: BackgroundMotion = 'none';
60
+
61
+ /** Strength of the contrast gradient above the image. */
62
+ @property({reflect: true}) overlay: BackgroundOverlay = 'soft';
63
+
64
+ /** Whether the overlay uses a light or dark contrast treatment. */
65
+ @property({attribute: 'overlay-tone', reflect: true}) overlayTone: BackgroundOverlayTone = 'light';
66
+
67
+ /** Comma-separated Zinc icon names placed as ambient decoration. At most eight are rendered. */
68
+ @property({attribute: 'floating-icons'}) floatingIcons = '';
69
+
70
+ /** Pauses background motion without changing the selected motion treatment. */
71
+ @property({type: Boolean, reflect: true}) paused = false;
72
+
73
+ render() {
74
+ const floatingIcons = this.floatingIcons
75
+ .split(',')
76
+ .map(icon => icon.trim())
77
+ .filter(Boolean)
78
+ .slice(0, 8);
79
+ const floatingIconSize = this.resizeObserver.value ?? 1;
80
+
81
+ return html`
82
+ <div
83
+ part="base"
84
+ class="background"
85
+ style=${styleMap({'--_zn-background-color': this.color || undefined})}>
86
+ ${this.image ? html`
87
+ <img
88
+ part="image"
89
+ class="background__image"
90
+ src=${this.image}
91
+ alt=""
92
+ decoding="async">
93
+ ` : ''}
94
+ <div part="overlay" class="background__overlay" aria-hidden="true"></div>
95
+ ${floatingIcons.length ? html`
96
+ <div part="floating-icons" class="background__floating-icons" aria-hidden="true">
97
+ ${floatingIcons.map(icon => html`
98
+ <zn-icon
99
+ part="floating-icon"
100
+ class="background__floating-icon"
101
+ src=${icon}
102
+ size=${floatingIconSize}
103
+ aria-hidden="true"></zn-icon>
104
+ `)}
105
+ </div>
106
+ ` : ''}
107
+ <div part="content" class="background__content"><slot></slot></div>
108
+ </div>
109
+ `;
110
+ }
111
+ }
@@ -0,0 +1,236 @@
1
+ @use "../../wc";
2
+
3
+ :host {
4
+ display: block;
5
+ position: relative;
6
+ min-width: 0;
7
+ min-height: 0;
8
+ overflow: hidden;
9
+ isolation: isolate;
10
+ }
11
+
12
+ .background {
13
+ position: relative;
14
+ box-sizing: border-box;
15
+ width: 100%;
16
+ height: 100%;
17
+ min-height: inherit;
18
+ overflow: hidden;
19
+ background-color: var(--_zn-background-color, var(--zn-background-color, transparent));
20
+ }
21
+
22
+ .background__image,
23
+ .background__overlay,
24
+ .background__floating-icons {
25
+ position: absolute;
26
+ inset: 0;
27
+ width: 100%;
28
+ height: 100%;
29
+ pointer-events: none;
30
+ }
31
+
32
+ .background__image {
33
+ width: 108%;
34
+ height: 108%;
35
+ max-width: none;
36
+ inset: -4%;
37
+ object-fit: cover;
38
+ object-position: var(--zn-background-image-position, center);
39
+ opacity: 1;
40
+ }
41
+
42
+ :host([image-strength="soft"]) .background__image {
43
+ opacity: 0.3;
44
+ }
45
+
46
+ :host([image-strength="medium"]) .background__image {
47
+ opacity: 0.62;
48
+ }
49
+
50
+ .background__overlay {
51
+ background: linear-gradient(
52
+ var(--zn-background-overlay-angle, 110deg),
53
+ rgb(255 255 255 / 18%),
54
+ rgb(255 255 255 / 4%)
55
+ );
56
+ }
57
+
58
+ :host([overlay="none"]) .background__overlay {
59
+ display: none;
60
+ }
61
+
62
+ :host([overlay="strong"]) .background__overlay {
63
+ background: linear-gradient(
64
+ var(--zn-background-overlay-angle, 110deg),
65
+ rgb(255 255 255 / 48%),
66
+ rgb(255 255 255 / 18%)
67
+ );
68
+ }
69
+
70
+ :host([overlay-tone="dark"]) .background__overlay {
71
+ background: linear-gradient(
72
+ var(--zn-background-overlay-angle, 110deg),
73
+ rgb(4 12 32 / 28%),
74
+ rgb(4 12 32 / 6%)
75
+ );
76
+ }
77
+
78
+ :host([overlay-tone="dark"][overlay="strong"]) .background__overlay {
79
+ background: linear-gradient(
80
+ var(--zn-background-overlay-angle, 110deg),
81
+ rgb(4 12 32 / 62%),
82
+ rgb(4 12 32 / 28%)
83
+ );
84
+ }
85
+
86
+ :host([motion="drift"]) .background__image {
87
+ animation: zn-background-drift 28s ease-in-out infinite alternate;
88
+ }
89
+
90
+ :host([motion="breathe"]) .background__image {
91
+ animation: zn-background-breathe 18s ease-in-out infinite;
92
+ }
93
+
94
+ :host([paused]) .background__image {
95
+ animation-play-state: paused;
96
+ }
97
+
98
+ .background__floating-icons {
99
+ overflow: hidden;
100
+ color: var(--zn-background-floating-icon-color, currentColor);
101
+ }
102
+
103
+ .background__floating-icon {
104
+ --_zn-background-icon-opacity-min: 0.01;
105
+ --_zn-background-icon-opacity-max: 0.18;
106
+ position: absolute;
107
+ left: 8%;
108
+ top: 13%;
109
+ opacity: var(--_zn-background-icon-opacity-min);
110
+ animation: zn-background-icon-float 11s ease-in-out infinite alternate both;
111
+ }
112
+
113
+ .background__floating-icon:nth-child(2) {
114
+ --_zn-background-icon-opacity-min: 0.08;
115
+ --_zn-background-icon-opacity-max: 0.34;
116
+ left: auto;
117
+ right: 9%;
118
+ top: 9%;
119
+ animation-delay: -4s;
120
+ animation-duration: 14s;
121
+ }
122
+
123
+ .background__floating-icon:nth-child(3) {
124
+ --_zn-background-icon-opacity-min: 0.03;
125
+ --_zn-background-icon-opacity-max: 0.5;
126
+ left: 2%;
127
+ top: 47%;
128
+ animation-delay: -7s;
129
+ animation-duration: 13s;
130
+ }
131
+
132
+ .background__floating-icon:nth-child(4) {
133
+ --_zn-background-icon-opacity-min: 0.12;
134
+ --_zn-background-icon-opacity-max: 0.38;
135
+ left: auto;
136
+ right: 12%;
137
+ top: 45%;
138
+ animation-delay: -2s;
139
+ animation-duration: 10s;
140
+ }
141
+
142
+ .background__floating-icon:nth-child(5) {
143
+ --_zn-background-icon-opacity-min: 0.05;
144
+ --_zn-background-icon-opacity-max: 0.29;
145
+ left: 21%;
146
+ top: auto;
147
+ bottom: 4%;
148
+ animation-delay: -5s;
149
+ animation-duration: 15s;
150
+ }
151
+
152
+ .background__floating-icon:nth-child(6) {
153
+ --_zn-background-icon-opacity-min: 0.01;
154
+ --_zn-background-icon-opacity-max: 0.42;
155
+ left: auto;
156
+ right: 4%;
157
+ top: auto;
158
+ bottom: 3%;
159
+ animation-delay: -9s;
160
+ animation-duration: 12s;
161
+ }
162
+
163
+ .background__floating-icon:nth-child(7) {
164
+ --_zn-background-icon-opacity-min: 0.09;
165
+ --_zn-background-icon-opacity-max: 0.31;
166
+ left: 42%;
167
+ top: 7%;
168
+ animation-delay: -6s;
169
+ animation-duration: 16s;
170
+ }
171
+
172
+ .background__floating-icon:nth-child(8) {
173
+ --_zn-background-icon-opacity-min: 0.04;
174
+ --_zn-background-icon-opacity-max: 0.46;
175
+ left: 54%;
176
+ top: auto;
177
+ bottom: 8%;
178
+ animation-delay: -11s;
179
+ animation-duration: 17s;
180
+ }
181
+
182
+ :host([paused]) .background__floating-icon {
183
+ animation-play-state: paused;
184
+ }
185
+
186
+ .background__content {
187
+ position: relative;
188
+ z-index: 1;
189
+ box-sizing: border-box;
190
+ width: 100%;
191
+ height: 100%;
192
+ min-height: inherit;
193
+ }
194
+
195
+ @keyframes zn-background-drift {
196
+ from {
197
+ transform: scale(1.04) translate3d(-1.2%, -0.6%, 0);
198
+ }
199
+
200
+ to {
201
+ transform: scale(1.04) translate3d(1.2%, 0.6%, 0);
202
+ }
203
+ }
204
+
205
+ @keyframes zn-background-breathe {
206
+ 0%, 100% {
207
+ transform: scale(1.02);
208
+ }
209
+
210
+ 50% {
211
+ transform: scale(1.07);
212
+ }
213
+ }
214
+
215
+ @keyframes zn-background-icon-float {
216
+ from {
217
+ opacity: var(--_zn-background-icon-opacity-min);
218
+ transform: translate3d(-8%, -12%, 0) scale(0.88) rotate(-8deg);
219
+ }
220
+
221
+ to {
222
+ opacity: var(--_zn-background-icon-opacity-max);
223
+ transform: translate3d(8%, 12%, 0) scale(1.06) rotate(8deg);
224
+ }
225
+ }
226
+
227
+ @media (prefers-reduced-motion: reduce) {
228
+ .background__image,
229
+ .background__floating-icon {
230
+ animation: none;
231
+ }
232
+
233
+ .background__floating-icon {
234
+ opacity: var(--_zn-background-icon-opacity-max);
235
+ }
236
+ }
@@ -0,0 +1,120 @@
1
+ import '../../../dist/zn.min.js';
2
+ import {expect, fixture, html, waitUntil} from '@open-wc/testing';
3
+ import type ZnBackground from './background.component';
4
+
5
+ const transparentImage = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
6
+
7
+ describe('<zn-background>', () => {
8
+ it('renders slotted content above the background layers', async () => {
9
+ const el = await fixture<ZnBackground>(html`
10
+ <zn-background><p>Lesson content</p></zn-background>`);
11
+
12
+ const content = el.shadowRoot!.querySelector<HTMLElement>('.background__content')!;
13
+ const slot = content.querySelector<HTMLSlotElement>('slot')!;
14
+
15
+ expect(slot.assignedElements()[0].textContent).to.equal('Lesson content');
16
+ expect(getComputedStyle(content).zIndex).to.equal('1');
17
+ });
18
+
19
+ it('composes the configured colour and decorative image', async () => {
20
+ const el = await fixture<ZnBackground>(html`
21
+ <zn-background color="#123456" image=${transparentImage}></zn-background>`);
22
+
23
+ const base = el.shadowRoot!.querySelector<HTMLElement>('.background')!;
24
+ const image = el.shadowRoot!.querySelector<HTMLImageElement>('.background__image')!;
25
+
26
+ expect(getComputedStyle(base).backgroundColor).to.equal('rgb(18, 52, 86)');
27
+ expect(image.src).to.equal(transparentImage);
28
+ expect(image.alt).to.equal('');
29
+ expect(image.getAttribute('part')).to.equal('image');
30
+ });
31
+
32
+ it('does not render an image element without an image URL', async () => {
33
+ const el = await fixture<ZnBackground>(html`<zn-background></zn-background>`);
34
+
35
+ expect(el.shadowRoot!.querySelector('.background__image')).to.not.exist;
36
+ });
37
+
38
+ it('applies soft, medium and full image strengths', async () => {
39
+ const el = await fixture<ZnBackground>(html`
40
+ <zn-background image=${transparentImage} image-strength="soft"></zn-background>`);
41
+ const image = el.shadowRoot!.querySelector<HTMLImageElement>('.background__image')!;
42
+
43
+ expect(getComputedStyle(image).opacity).to.equal('0.3');
44
+ el.imageStrength = 'medium';
45
+ await el.updateComplete;
46
+ expect(getComputedStyle(image).opacity).to.equal('0.62');
47
+ el.imageStrength = 'full';
48
+ await el.updateComplete;
49
+ expect(getComputedStyle(image).opacity).to.equal('1');
50
+ });
51
+
52
+ it('supports drift and breathe motion and can pause it', async () => {
53
+ const el = await fixture<ZnBackground>(html`
54
+ <zn-background image=${transparentImage} motion="drift"></zn-background>`);
55
+ const image = el.shadowRoot!.querySelector<HTMLImageElement>('.background__image')!;
56
+
57
+ expect(getComputedStyle(image).animationName).to.equal('zn-background-drift');
58
+ el.motion = 'breathe';
59
+ el.paused = true;
60
+ await el.updateComplete;
61
+ expect(getComputedStyle(image).animationName).to.equal('zn-background-breathe');
62
+ expect(getComputedStyle(image).animationPlayState).to.equal('paused');
63
+ });
64
+
65
+ it('supports none, soft and strong light or dark overlays', async () => {
66
+ const el = await fixture<ZnBackground>(html`
67
+ <zn-background overlay="none"></zn-background>`);
68
+ const overlay = el.shadowRoot!.querySelector<HTMLElement>('.background__overlay')!;
69
+
70
+ expect(getComputedStyle(overlay).display).to.equal('none');
71
+ el.overlay = 'soft';
72
+ await el.updateComplete;
73
+ const lightGradient = getComputedStyle(overlay).backgroundImage;
74
+ expect(lightGradient).to.contain('255, 255, 255');
75
+
76
+ el.overlay = 'strong';
77
+ el.overlayTone = 'dark';
78
+ await el.updateComplete;
79
+ const darkGradient = getComputedStyle(overlay).backgroundImage;
80
+ expect(darkGradient).to.contain('4, 12, 32');
81
+ expect(darkGradient).to.not.equal(lightGradient);
82
+ });
83
+
84
+ it('renders up to eight comma-separated floating Zinc icons', async () => {
85
+ const el = await fixture<ZnBackground>(html`
86
+ <zn-background
87
+ floating-icons="school, lightbulb, quiz, psychology, star, rocket, science, public, hidden">
88
+ </zn-background>`);
89
+ const icons = el.shadowRoot!.querySelectorAll<HTMLElement>('.background__floating-icon');
90
+
91
+ expect(icons).to.have.lengthOf(8);
92
+ expect(icons[0].getAttribute('src')).to.equal('school');
93
+ expect(icons[7].getAttribute('src')).to.equal('public');
94
+ expect(el.shadowRoot!.querySelector('.background__floating-icons')!.getAttribute('aria-hidden')).to.equal('true');
95
+ });
96
+
97
+ it('sizes floating icons to five percent of the background width', async () => {
98
+ const el = await fixture<ZnBackground>(html`
99
+ <zn-background floating-icons="school" style="width: 1000px;"></zn-background>`);
100
+ const icon = el.shadowRoot!.querySelector<HTMLElement & {size: number}>('.background__floating-icon')!;
101
+
102
+ await waitUntil(() => icon.size === 50);
103
+ expect(icon.size).to.equal(50);
104
+ });
105
+
106
+ it('caps floating icons at 150 pixels and keeps opacity within one to fifty percent', async () => {
107
+ const el = await fixture<ZnBackground>(html`
108
+ <zn-background floating-icons="school,lightbulb" style="width: 4000px;"></zn-background>`);
109
+ const icons = [...el.shadowRoot!.querySelectorAll<HTMLElement & {size: number}>('.background__floating-icon')];
110
+
111
+ await waitUntil(() => icons[0].size === 150);
112
+ expect(icons[0].size).to.equal(150);
113
+ icons.forEach(icon => {
114
+ const styles = getComputedStyle(icon);
115
+ expect(Number(styles.getPropertyValue('--_zn-background-icon-opacity-min'))).to.be.at.least(0.01);
116
+ expect(Number(styles.getPropertyValue('--_zn-background-icon-opacity-max'))).to.be.at.most(0.5);
117
+ expect(styles.animationName).to.equal('zn-background-icon-float');
118
+ });
119
+ });
120
+ });
@@ -0,0 +1,12 @@
1
+ import ZnBackground from './background.component';
2
+
3
+ export * from './background.component';
4
+ export default ZnBackground;
5
+
6
+ ZnBackground.define('zn-background');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-background': ZnBackground;
11
+ }
12
+ }
@@ -17,7 +17,12 @@
17
17
  .button {
18
18
  @include shared.button;
19
19
 
20
+ // --btn-color paints the fill; --btn-ink is the text/icon colour that sits
21
+ // on top of it. They are declared as a pair so a theme with a light accent
22
+ // (e.g. neon lime) can re-point the ink via --zn-color-on-* without any
23
+ // component-level override.
20
24
  --btn-color: var(--zn-primary);
25
+ --btn-ink: var(--zn-color-on-primary, 255, 255, 255);
21
26
  --notification-color: rgb(var(--zn-color-error));
22
27
 
23
28
  border-width: var(--zn-button-border-width, 1px);
@@ -31,18 +36,22 @@
31
36
 
32
37
  &.button--info {
33
38
  --btn-color: var(--zn-color-info);
39
+ --btn-ink: var(--zn-color-on-info, 255, 255, 255);
34
40
  }
35
41
 
36
42
  &.button--error {
37
43
  --btn-color: var(--zn-color-error);
44
+ --btn-ink: var(--zn-color-on-error, 255, 255, 255);
38
45
  }
39
46
 
40
47
  &.button--warning {
41
48
  --btn-color: var(--zn-color-warning);
49
+ --btn-ink: var(--zn-color-on-warning, 255, 255, 255);
42
50
  }
43
51
 
44
52
  &.button--success {
45
53
  --btn-color: var(--zn-color-success);
54
+ --btn-ink: var(--zn-color-on-success, 255, 255, 255);
46
55
  }
47
56
 
48
57
  &__label--hidden {
@@ -131,7 +140,7 @@
131
140
 
132
141
  background-color: rgba(var(--btn-color));
133
142
  border-color: var(--zn-button-border-color, rgb(var(--btn-color)));
134
- color: rgba(255, 255, 255, 0.9);
143
+ color: rgba(var(--btn-ink), 0.9);
135
144
 
136
145
  &:hover:not([disabled]) {
137
146
  background-color: rgba(var(--btn-color), 0.8);
@@ -154,7 +163,7 @@
154
163
 
155
164
  &:hover:not([disabled]), &:active:not([disabled]) {
156
165
  background-color: rgb(var(--btn-color));
157
- color: white;
166
+ color: rgb(var(--btn-ink));
158
167
  }
159
168
 
160
169
  &.button--secondary {
@@ -219,9 +228,10 @@
219
228
 
220
229
  .button--star {
221
230
  --btn-color: var(--zn-orange);
231
+ --btn-ink: var(--zn-color-on-warning, 255, 255, 255);
222
232
  background-color: rgb(var(--btn-color));
223
233
  border-color: var(--zn-button-border-color, rgb(var(--btn-color)));
224
- color: white;
234
+ color: rgb(var(--btn-ink));
225
235
  opacity: 1 !important;
226
236
 
227
237
  &:hover:not([disabled]) {
@@ -239,7 +249,7 @@
239
249
 
240
250
  &:hover:not([disabled]), &:active:not([disabled]) {
241
251
  background-color: rgba(var(--btn-color));
242
- color: white;
252
+ color: rgb(var(--btn-ink));
243
253
  }
244
254
  }
245
255
  }