@dataengineeringformachinelearning/viking-ui 1.0.2 → 3.0.0-alpha.2

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 (129) hide show
  1. package/README.md +180 -41
  2. package/dist/deml-components.css +1122 -0
  3. package/dist/design-tokens.css +688 -0
  4. package/dist/elements/button/viking-button.d.ts +14 -0
  5. package/dist/elements/card/viking-card.d.ts +8 -0
  6. package/dist/elements/core/dom.d.ts +4 -0
  7. package/dist/elements/core/styles.d.ts +2 -0
  8. package/dist/elements/modal/viking-modal.d.ts +15 -0
  9. package/dist/fonts/inter/InterVariable-Italic.woff2 +0 -0
  10. package/dist/fonts/inter/InterVariable.woff2 +0 -0
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +4051 -0
  13. package/dist/lib/core/brand-icons.d.ts +29 -0
  14. package/dist/lib/core/icons.d.ts +132 -0
  15. package/dist/lib/core/integration-brand-icons.d.ts +18 -0
  16. package/dist/lib/core/lucide-paths.generated.d.ts +90 -0
  17. package/dist/lib/site-drakkar/site-drakkar.config.d.ts +50 -0
  18. package/dist/lib/site-drakkar/suite-search-items.d.ts +17 -0
  19. package/dist/viking-components.css +1837 -0
  20. package/dist/viking-tokens.json +318 -0
  21. package/dist/viking-ui-elements.js +1750 -0
  22. package/dist/viking-ui.css +1 -0
  23. package/dist/web/badge/viking-badge-wc.d.ts +28 -0
  24. package/dist/web/button/viking-button-wc.d.ts +28 -0
  25. package/dist/web/callout/viking-callout-wc.d.ts +27 -0
  26. package/dist/web/card/viking-card-wc.d.ts +29 -0
  27. package/dist/web/core/base.d.ts +12 -0
  28. package/dist/web/core/dom.d.ts +11 -0
  29. package/dist/web/core/icons-inline.d.ts +7 -0
  30. package/dist/web/core/styles.d.ts +12 -0
  31. package/dist/web/core/types.d.ts +10 -0
  32. package/dist/web/field/viking-field-wc.d.ts +32 -0
  33. package/dist/web/index.d.ts +15 -0
  34. package/dist/web/input/viking-input-wc.d.ts +32 -0
  35. package/dist/web/modal/viking-modal-wc.d.ts +50 -0
  36. package/dist/web/search-palette/viking-search-palette-wc.d.ts +64 -0
  37. package/dist/web/select/viking-select-wc.d.ts +48 -0
  38. package/dist/web/suite-header/viking-suite-header-wc.d.ts +36 -0
  39. package/dist/web/suite-search-palette/viking-suite-search-palette-wc.d.ts +39 -0
  40. package/dist/web/theme-toggle/viking-theme-toggle-wc.d.ts +19 -0
  41. package/dist/web/types.d.ts +40 -0
  42. package/dist/web-components/index.d.ts +1 -0
  43. package/dist/web-components.js +1750 -0
  44. package/dist/widget.js +1182 -0
  45. package/package.json +89 -30
  46. package/src/assets/fonts/inter/InterVariable-Italic.woff2 +0 -0
  47. package/src/assets/fonts/inter/InterVariable.woff2 +0 -0
  48. package/src/elements/button/viking-button.ts +201 -0
  49. package/src/elements/card/viking-card.ts +60 -0
  50. package/src/elements/core/dom.ts +33 -0
  51. package/src/elements/core/styles.ts +28 -0
  52. package/src/elements/modal/viking-modal.ts +154 -0
  53. package/src/index.ts +1 -0
  54. package/src/lib/core/brand-icons.ts +62 -0
  55. package/src/lib/core/icons.ts +206 -0
  56. package/src/lib/core/integration-brand-icons.ts +77 -0
  57. package/src/lib/core/lucide-paths.generated.ts +130 -0
  58. package/src/lib/site-drakkar/site-drakkar.config.ts +295 -0
  59. package/src/lib/site-drakkar/suite-search-items.ts +246 -0
  60. package/src/styles/_buttons.scss +301 -0
  61. package/src/styles/_component-primitives.scss +183 -0
  62. package/src/styles/_drakkar-buttons.scss +85 -0
  63. package/src/styles/_fonts.scss +22 -0
  64. package/src/styles/_forms.scss +66 -0
  65. package/src/styles/_input.scss +106 -0
  66. package/src/styles/_layout-enforcement.scss +166 -0
  67. package/src/styles/_layout-rhythm.scss +158 -0
  68. package/src/styles/_legacy-aliases.scss +212 -0
  69. package/src/styles/_series-colors.scss +24 -0
  70. package/src/styles/_static-primitives.scss +857 -0
  71. package/src/styles/_typography.scss +87 -0
  72. package/src/styles/_variables.scss +527 -0
  73. package/src/styles/components/a11y.scss +90 -0
  74. package/src/styles/components/badges.scss +102 -0
  75. package/src/styles/components/extracted-utilities.scss +459 -0
  76. package/src/styles/components/footer.scss +155 -0
  77. package/src/styles/components/icon-heading.scss +47 -0
  78. package/src/styles/components/icon-inline.scss +78 -0
  79. package/src/styles/components/layout.scss +59 -0
  80. package/src/styles/components/utilities.scss +131 -0
  81. package/src/styles/components/viking-inspired.scss +204 -0
  82. package/src/styles/components/whitepaper-cta.scss +130 -0
  83. package/src/styles/components-bundle.scss +9 -0
  84. package/src/styles/components.scss +175 -0
  85. package/src/styles/deml-components.scss +10 -0
  86. package/src/styles/index.scss +8 -0
  87. package/src/styles/layout-shell.scss +39 -11
  88. package/src/styles/page-shell.scss +663 -33
  89. package/src/styles/static-navbar.scss +862 -146
  90. package/src/styles/surfaces/backend.scss +142 -0
  91. package/src/styles/surfaces/docs-global.scss +73 -0
  92. package/src/styles/surfaces/docs-shell.scss +412 -0
  93. package/src/styles/surfaces/docs-showcase.scss +1051 -0
  94. package/src/styles/surfaces/marketing-cta.scss +62 -0
  95. package/src/styles/surfaces/marketing-docs-bento.scss +67 -0
  96. package/src/styles/surfaces/marketing-global.scss +748 -0
  97. package/src/styles/surfaces/marketing-landing.scss +1260 -0
  98. package/src/styles/surfaces/marketing-publication.scss +535 -0
  99. package/src/styles/surfaces/marketing-theme.scss +14 -0
  100. package/src/styles/surfaces/swagger-ui.scss +921 -0
  101. package/src/styles/tokens-export.scss +18 -0
  102. package/src/styles/tokens.scss +5 -0
  103. package/src/styles/viking-ui-bundle.scss +20 -2
  104. package/src/styles/viking-ui.scss +213 -51
  105. package/src/tokens/viking-tokens.json +318 -0
  106. package/src/web/badge/viking-badge-wc.ts +116 -0
  107. package/src/web/button/viking-button-wc.ts +165 -0
  108. package/src/web/callout/viking-callout-wc.ts +105 -0
  109. package/src/web/card/viking-card-wc.ts +66 -0
  110. package/src/web/core/base.ts +56 -0
  111. package/src/web/core/dom.ts +53 -0
  112. package/src/web/core/icons-inline.ts +44 -0
  113. package/src/web/core/styles.ts +1138 -0
  114. package/src/web/core/types.ts +19 -0
  115. package/src/web/field/viking-field-wc.ts +154 -0
  116. package/src/web/index.ts +73 -0
  117. package/src/web/input/viking-input-wc.ts +211 -0
  118. package/src/web/modal/viking-modal-wc.ts +187 -0
  119. package/src/web/search-palette/viking-search-palette-wc.ts +494 -0
  120. package/src/web/select/viking-select-wc.ts +218 -0
  121. package/src/web/suite-header/viking-suite-header-wc.ts +714 -0
  122. package/src/web/suite-search-palette/viking-suite-search-palette-wc.ts +234 -0
  123. package/src/web/theme-toggle/viking-theme-toggle-wc.ts +139 -0
  124. package/src/web/types.ts +37 -0
  125. package/src/web-components/index.ts +1 -0
  126. package/fesm2022/dataengineeringformachinelearning-viking-ui.mjs +0 -6114
  127. package/fesm2022/dataengineeringformachinelearning-viking-ui.mjs.map +0 -1
  128. package/types/dataengineeringformachinelearning-viking-ui.d.ts +0 -1397
  129. package/viking.manifest.json +0 -238
@@ -0,0 +1,318 @@
1
+ {
2
+ "$schema": "./viking-tokens.schema.json",
3
+ "meta": {
4
+ "name": "viking-ui",
5
+ "version": "2.1.0",
6
+ "description": "Canonical DEML design tokens — single source of truth for primitives, semantic aliases, and component foundations",
7
+ "canonicalScss": "../styles/_variables.scss",
8
+ "documentation": "../../../THEME.md"
9
+ },
10
+ "color": {
11
+ "navy": {
12
+ "1000": "#01030a",
13
+ "950": "#030818",
14
+ "900": "#0a1024",
15
+ "800": "#101b33",
16
+ "700": "#162544",
17
+ "600": "#1e3054",
18
+ "500": "#284068",
19
+ "400": "#39547f",
20
+ "300": "#58719e",
21
+ "200": "#8fa2c2",
22
+ "100": "#d7e0ef"
23
+ },
24
+ "charcoal": {
25
+ "950": "{color.navy.950}",
26
+ "900": "{color.navy.900}",
27
+ "800": "{color.navy.800}",
28
+ "700": "{color.navy.700}",
29
+ "600": "{color.navy.600}",
30
+ "500": "{color.navy.500}",
31
+ "400": "{color.navy.400}",
32
+ "300": "{color.navy.300}",
33
+ "200": "{color.navy.200}",
34
+ "100": "{color.navy.100}"
35
+ },
36
+ "electric": {
37
+ "900": "#04225f",
38
+ "800": "#073a99",
39
+ "700": "#0a4fd4",
40
+ "600": "#1565f0",
41
+ "500": "#2176ff",
42
+ "400": "#4d94ff",
43
+ "300": "#7ab0ff",
44
+ "200": "#b6d3ff",
45
+ "100": "#e7f1ff"
46
+ },
47
+ "metallic": {
48
+ "600": "#555555",
49
+ "500": "#666666",
50
+ "400": "#777777",
51
+ "300": "#999999",
52
+ "200": "#aaaaaa",
53
+ "100": "#bbbbbb",
54
+ "050": "#e6e8ec"
55
+ },
56
+ "teal": {
57
+ "700": "{color.electric.700}",
58
+ "600": "{color.electric.600}",
59
+ "500": "{color.electric.500}",
60
+ "400": "{color.electric.400}",
61
+ "300": "{color.electric.300}"
62
+ },
63
+ "crimson": {
64
+ "700": "#7a2231",
65
+ "600": "#922b3e",
66
+ "500": "#a83344",
67
+ "400": "#c44355",
68
+ "300": "#dc6b78",
69
+ "100": "#fde8ec"
70
+ },
71
+ "cyan": { "600": "#168aad", "500": "#1fa2c9", "400": "#47bee0" },
72
+ "violet": { "600": "#6f5bd7", "500": "#8674f0" },
73
+ "amber": { "600": "#a77912", "500": "#d69e2e" },
74
+ "green": { "500": "#2a9d8f" },
75
+ "gold": { "500": "#c4a035" },
76
+ "blue": { "500": "{color.electric.400}" },
77
+ "red": { "500": "{color.crimson.500}" },
78
+ "ink": {
79
+ "1000": "#000000",
80
+ "950": "#050609",
81
+ "900": "#0c0f14",
82
+ "800": "#151a22",
83
+ "700": "#202735"
84
+ },
85
+ "slate": { "100": "#f4f7fb", "050": "#fbfcfe" },
86
+ "white": "#f5f5f5",
87
+ "whitePure": "#ffffff",
88
+ "black": "#000000"
89
+ },
90
+ "series": {
91
+ "1": {
92
+ "hex": "#2176ff",
93
+ "token": "--viking-series-1",
94
+ "role": "Primary series, default selection"
95
+ },
96
+ "2": {
97
+ "hex": "#922b3e",
98
+ "token": "--viking-series-2",
99
+ "role": "Secondary / comparison series"
100
+ },
101
+ "3": {
102
+ "hex": "#2a9d8f",
103
+ "token": "--viking-series-3",
104
+ "role": "Success / stable metrics"
105
+ },
106
+ "4": {
107
+ "hex": "#c4a035",
108
+ "token": "--viking-series-4",
109
+ "role": "Warning / threshold proximity"
110
+ },
111
+ "5": {
112
+ "hex": "#a83344",
113
+ "token": "--viking-series-5",
114
+ "role": "Critical / anomaly series"
115
+ },
116
+ "6": {
117
+ "hex": "#4d94ff",
118
+ "token": "--viking-series-6",
119
+ "role": "Info / auxiliary series"
120
+ },
121
+ "7": {
122
+ "hex": "#162544",
123
+ "token": "--viking-series-7",
124
+ "role": "Baseline / muted series"
125
+ },
126
+ "8": {
127
+ "hex": "#666666",
128
+ "token": "--viking-series-8",
129
+ "role": "Disabled / archived series"
130
+ },
131
+ "default": "#2176ff"
132
+ },
133
+ "semantic": {
134
+ "dark": {
135
+ "bg": "navy.900",
136
+ "bgSubtle": "ink.950",
137
+ "surface": "navy.800",
138
+ "surfaceAlt": "navy.700",
139
+ "surfaceInset": "navy.950",
140
+ "surfaceRaised": "navy.600",
141
+ "surfaceOverlay": "ink.800",
142
+ "text": "white",
143
+ "textMuted": "metallic.200",
144
+ "textSubtle": "metallic.300",
145
+ "textDisabled": "metallic.400",
146
+ "textLink": "electric.300",
147
+ "accent": "electric.500",
148
+ "accentHover": "electric.400",
149
+ "accentActive": "electric.700",
150
+ "accentSoft": "color-mix(in srgb, #2176ff 18%, transparent)",
151
+ "accentSecondary": "crimson.600",
152
+ "success": "green.500",
153
+ "warning": "gold.500",
154
+ "danger": "crimson.500",
155
+ "info": "blue.500",
156
+ "ring": "electric.400"
157
+ },
158
+ "light": {
159
+ "bg": "slate.050",
160
+ "bgSubtle": "slate.100",
161
+ "surface": "whitePure",
162
+ "surfaceAlt": "slate.100",
163
+ "surfaceInset": "metallic.050",
164
+ "surfaceRaised": "whitePure",
165
+ "surfaceOverlay": "whitePure",
166
+ "text": "navy.900",
167
+ "textMuted": "metallic.500",
168
+ "textSubtle": "metallic.400",
169
+ "textDisabled": "metallic.300",
170
+ "textLink": "electric.700",
171
+ "accent": "electric.600",
172
+ "accentHover": "electric.500",
173
+ "accentActive": "electric.700",
174
+ "accentSoft": "color-mix(in srgb, #1565f0 14%, transparent)",
175
+ "accentSecondary": "crimson.700",
176
+ "success": "green.500",
177
+ "warning": "gold.500",
178
+ "danger": "crimson.500",
179
+ "info": "blue.500",
180
+ "ring": "electric.500"
181
+ }
182
+ },
183
+ "typography": {
184
+ "fontFamily": {
185
+ "sans": "'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
186
+ "mono": "ui-monospace, 'JetBrains Mono', 'SF Mono', 'Cascadia Code', Consolas, monospace"
187
+ },
188
+ "fontSize": {
189
+ "2xs": "11px",
190
+ "xs": "12px",
191
+ "sm": "14px",
192
+ "base": "16px",
193
+ "md": "18px",
194
+ "lg": "20px",
195
+ "xl": "24px",
196
+ "2xl": "30px",
197
+ "3xl": "36px",
198
+ "4xl": "48px",
199
+ "5xl": "60px",
200
+ "6xl": "72px"
201
+ },
202
+ "fontWeight": {
203
+ "regular": 400,
204
+ "medium": 500,
205
+ "semibold": 600,
206
+ "bold": 700,
207
+ "extrabold": 800
208
+ },
209
+ "lineHeight": {
210
+ "tight": 1.25,
211
+ "snug": 1.375,
212
+ "normal": 1.5,
213
+ "relaxed": 1.625,
214
+ "loose": 1.75
215
+ },
216
+ "letterSpacing": {
217
+ "tighter": "-0.03em",
218
+ "tight": "-0.02em",
219
+ "normal": "0",
220
+ "wide": "0.025em",
221
+ "wider": "0.05em",
222
+ "caps": "0.08em"
223
+ }
224
+ },
225
+ "spacing": {
226
+ "gridUnit": "4px",
227
+ "spaceUnit": "8px",
228
+ "0": "0",
229
+ "px": "1px",
230
+ "half": "4px",
231
+ "0.5": "4px",
232
+ "1": "8px",
233
+ "1.5": "12px",
234
+ "2": "16px",
235
+ "2.5": "20px",
236
+ "3": "24px",
237
+ "4": "32px",
238
+ "5": "40px",
239
+ "6": "48px",
240
+ "7": "56px",
241
+ "8": "64px",
242
+ "8.5": "72px",
243
+ "9": "80px",
244
+ "10": "96px",
245
+ "11": "112px",
246
+ "12": "128px",
247
+ "14": "160px",
248
+ "16": "192px",
249
+ "20": "256px",
250
+ "24": "320px"
251
+ },
252
+ "radius": {
253
+ "xs": "4px",
254
+ "sm": "6px",
255
+ "DEFAULT": "8px",
256
+ "md": "10px",
257
+ "lg": "12px",
258
+ "xl": "16px",
259
+ "2xl": "24px",
260
+ "pill": "999px",
261
+ "full": "999px"
262
+ },
263
+ "shadow": {
264
+ "xs": "0 1px 2px rgba(0, 0, 0, 0.2)",
265
+ "sm": "0 1px 2px rgba(0,0,0,0.18), 0 1px 3px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.04)",
266
+ "md": "0 2px 4px rgba(0,0,0,0.16), 0 4px 8px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.05)",
267
+ "lg": "0 4px 8px rgba(0,0,0,0.14), 0 12px 24px rgba(0,0,0,0.18), inset 0 1px 0 rgba(255,255,255,0.06)",
268
+ "inner": "inset 0 1px 2px rgba(0, 0, 0, 0.14)",
269
+ "hover": "0 8px 16px rgba(0,0,0,0.14), 0 4px 8px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.06)",
270
+ "focus": "0 0 0 var(--viking-ring-offset) var(--viking-bg), 0 0 0 calc(var(--viking-ring-offset) + var(--viking-ring-width)) var(--viking-ring)",
271
+ "surfaceHighlight": "inset 0 1px 0 rgba(255,255,255,0.06)"
272
+ },
273
+ "motion": {
274
+ "ease": {
275
+ "default": "cubic-bezier(0.4, 0, 0.2, 1)",
276
+ "in": "cubic-bezier(0.4, 0, 1, 1)",
277
+ "out": "cubic-bezier(0, 0, 0.2, 1)",
278
+ "emphasized": "cubic-bezier(0.2, 0, 0, 1)"
279
+ },
280
+ "duration": {
281
+ "instant": "75ms",
282
+ "fast": "150ms",
283
+ "DEFAULT": "250ms",
284
+ "slow": "350ms",
285
+ "slower": "500ms"
286
+ },
287
+ "state": {
288
+ "hoverLift": "-1px",
289
+ "activeScale": "0.985",
290
+ "disabledOpacity": "0.48"
291
+ }
292
+ },
293
+ "layout": {
294
+ "containerMaxWidth": "1260px",
295
+ "navbarHeight": "64px",
296
+ "sidebarWidth": "256px",
297
+ "controlHeight": "40px",
298
+ "controlHeightSm": "32px",
299
+ "controlHeightXs": "24px",
300
+ "btnMinWidth": "120px",
301
+ "touchTargetMin": "44px",
302
+ "pageGutter": "16px",
303
+ "pageGutterLg": "24px",
304
+ "pageStackGap": "24px",
305
+ "pageSectionGap": "32px",
306
+ "cardPadding": "24px",
307
+ "cardPaddingCompact": "16px",
308
+ "panelPadding": "24px",
309
+ "formMaxWidth": "42rem",
310
+ "formNarrowMaxWidth": "28rem",
311
+ "contentReadableMaxWidth": "48rem"
312
+ },
313
+ "zIndex": {
314
+ "overlay": 1000,
315
+ "toast": 1100,
316
+ "tooltip": 1200
317
+ }
318
+ }
@@ -0,0 +1,116 @@
1
+ import { attachShadowStyles } from "../core/base";
2
+ import {
3
+ defineCustomElement,
4
+ defineCustomElementAlias,
5
+ HTMLElementBase,
6
+ } from "../core/dom";
7
+ import { renderInlineIcon, TONE_ICON_NAMES } from "../core/icons-inline";
8
+ import { VIKING_BADGE_STYLES } from "../core/styles";
9
+ import type { VikingWcTone } from "../core/types";
10
+
11
+ const TONES = new Set<VikingWcTone>([
12
+ "accent",
13
+ "secondary",
14
+ "success",
15
+ "warning",
16
+ "danger",
17
+ "info",
18
+ "muted",
19
+ "subtle",
20
+ ]);
21
+
22
+ /**
23
+ * Framework-agnostic Viking badge Web Component.
24
+ * Tag: `viking-badge` (legacy alias: `viking-badge-wc`)
25
+ *
26
+ * @attr tone - Semantic color: accent | secondary | success | warning | danger | info | muted
27
+ * @attr size - Compact density: sm
28
+ * @attr icon - Viking icon registry name (pairs with label for a11y)
29
+ * @attr removable - Shows remove control; dispatches `viking-removed`
30
+ *
31
+ * @example
32
+ * <viking-badge-wc tone="success" icon="check">Healthy</viking-badge-wc>
33
+ */
34
+ export class VikingBadgeWc extends HTMLElementBase {
35
+ static readonly tag = "viking-badge";
36
+ static readonly legacyTag = "viking-badge-wc";
37
+
38
+ static get observedAttributes(): string[] {
39
+ return ["tone", "size", "icon", "removable"];
40
+ }
41
+
42
+ private readonly shadow: ShadowRoot;
43
+
44
+ constructor() {
45
+ super();
46
+ this.shadow = this.attachShadow({ mode: "open" });
47
+ attachShadowStyles(this.shadow, VIKING_BADGE_STYLES);
48
+ }
49
+
50
+ connectedCallback(): void {
51
+ this.render();
52
+ }
53
+
54
+ attributeChangedCallback(): void {
55
+ if (this.isConnected) {
56
+ this.render();
57
+ }
58
+ }
59
+
60
+ private get tone(): VikingWcTone | null {
61
+ const value = (this.getAttribute("tone") ?? "") as VikingWcTone;
62
+ return TONES.has(value) ? value : null;
63
+ }
64
+
65
+ private get size(): "sm" | null {
66
+ return this.getAttribute("size") === "sm" ? "sm" : null;
67
+ }
68
+
69
+ private get removable(): boolean {
70
+ return (
71
+ this.hasAttribute("removable") &&
72
+ this.getAttribute("removable") !== "false"
73
+ );
74
+ }
75
+
76
+ private readonly onRemove = (): void => {
77
+ this.dispatchEvent(
78
+ new CustomEvent("viking-removed", { bubbles: true, composed: true }),
79
+ );
80
+ };
81
+
82
+ private render(): void {
83
+ const tone = this.tone;
84
+ if (tone && this.getAttribute("tone") !== tone) {
85
+ this.setAttribute("tone", tone);
86
+ } else if (!tone && this.hasAttribute("tone")) {
87
+ this.removeAttribute("tone");
88
+ }
89
+
90
+ const size = this.size;
91
+ if (size && this.getAttribute("size") !== size) {
92
+ this.setAttribute("size", size);
93
+ } else if (!size && this.hasAttribute("size")) {
94
+ this.removeAttribute("size");
95
+ }
96
+
97
+ const iconName =
98
+ this.getAttribute("icon") ?? (tone ? TONE_ICON_NAMES[tone] : null);
99
+ const iconMarkup = iconName ? renderInlineIcon(iconName, 16) : "";
100
+
101
+ this.shadow.innerHTML = `
102
+ ${iconMarkup}
103
+ <span part="label"><slot></slot></span>
104
+ ${this.removable ? `<button type="button" class="viking-badge-remove" part="remove" aria-label="Remove">${renderInlineIcon("x", 14)}</button>` : ""}
105
+ `;
106
+
107
+ this.shadow
108
+ .querySelector(".viking-badge-remove")
109
+ ?.addEventListener("click", this.onRemove);
110
+ }
111
+ }
112
+
113
+ export const registerVikingBadgeWc = (): void => {
114
+ defineCustomElement(VikingBadgeWc.tag, VikingBadgeWc);
115
+ defineCustomElementAlias(VikingBadgeWc.legacyTag, VikingBadgeWc);
116
+ };
@@ -0,0 +1,165 @@
1
+ import { attachShadowStyles, readBoolAttr } from "../core/base";
2
+ import {
3
+ defineCustomElement,
4
+ defineCustomElementAlias,
5
+ escapeHtml,
6
+ HTMLElementBase,
7
+ } from "../core/dom";
8
+ import { VIKING_BUTTON_STYLES } from "../core/styles";
9
+
10
+ const VARIANTS = new Set([
11
+ "outline",
12
+ "primary",
13
+ "secondary",
14
+ "filled",
15
+ "danger",
16
+ "ghost",
17
+ "subtle",
18
+ ]);
19
+
20
+ const SIZES = new Set(["sm", "xs"]);
21
+
22
+ /**
23
+ * Framework-agnostic Viking button Web Component.
24
+ * Tag: `viking-button` (legacy alias: `viking-button-wc`)
25
+ *
26
+ * @example
27
+ * <viking-button variant="primary">Launch</viking-button>
28
+ */
29
+ export class VikingButtonWc extends HTMLElementBase {
30
+ static readonly tag = "viking-button";
31
+ static readonly legacyTag = "viking-button-wc";
32
+
33
+ static get observedAttributes(): string[] {
34
+ return [
35
+ "variant",
36
+ "size",
37
+ "type",
38
+ "disabled",
39
+ "loading",
40
+ "href",
41
+ "target",
42
+ "aria-label",
43
+ "aria-busy",
44
+ "square",
45
+ "full-width",
46
+ "compact",
47
+ ];
48
+ }
49
+
50
+ private readonly shadow: ShadowRoot;
51
+ private control: HTMLButtonElement | HTMLAnchorElement | null = null;
52
+
53
+ constructor() {
54
+ super();
55
+ this.shadow = this.attachShadow({ mode: "open" });
56
+ attachShadowStyles(this.shadow, VIKING_BUTTON_STYLES);
57
+ }
58
+
59
+ connectedCallback(): void {
60
+ this.render();
61
+ this.syncHostSemantics();
62
+ this.control?.addEventListener("click", this.onClick);
63
+ }
64
+
65
+ disconnectedCallback(): void {
66
+ this.control?.removeEventListener("click", this.onClick);
67
+ }
68
+
69
+ attributeChangedCallback(): void {
70
+ if (this.isConnected) {
71
+ this.render();
72
+ this.syncHostSemantics();
73
+ }
74
+ }
75
+
76
+ private readonly onClick = (event: Event): void => {
77
+ if (this.disabled || this.loading) {
78
+ event.preventDefault();
79
+ event.stopPropagation();
80
+ return;
81
+ }
82
+ this.dispatchEvent(
83
+ new CustomEvent("viking-press", {
84
+ bubbles: true,
85
+ composed: true,
86
+ detail: event,
87
+ }),
88
+ );
89
+ };
90
+
91
+ private get variant(): string {
92
+ const value = this.getAttribute("variant") ?? "outline";
93
+ return VARIANTS.has(value) ? value : "outline";
94
+ }
95
+
96
+ private get size(): string | null {
97
+ const value = this.getAttribute("size");
98
+ return value && SIZES.has(value) ? value : null;
99
+ }
100
+
101
+ private get disabled(): boolean {
102
+ return readBoolAttr(this, "disabled");
103
+ }
104
+
105
+ private get loading(): boolean {
106
+ return readBoolAttr(this, "loading");
107
+ }
108
+
109
+ private get square(): boolean {
110
+ return readBoolAttr(this, "square");
111
+ }
112
+
113
+ private syncHostSemantics(): void {
114
+ if (!this.hasAttribute("role")) {
115
+ this.setAttribute("role", this.getAttribute("href") ? "link" : "button");
116
+ }
117
+ }
118
+
119
+ private render(): void {
120
+ const href = this.getAttribute("href");
121
+ const isLink = Boolean(href);
122
+ const tag = isLink ? "a" : "button";
123
+
124
+ const classes = [
125
+ "viking-btn",
126
+ `viking-btn-${this.variant}`,
127
+ this.size ? `viking-btn-${this.size}` : "",
128
+ this.square ? "viking-btn-square" : "",
129
+ ]
130
+ .filter(Boolean)
131
+ .join(" ");
132
+
133
+ const label = this.getAttribute("aria-label") ?? "";
134
+ const busy =
135
+ this.getAttribute("aria-busy") === "true" || this.loading ? "true" : null;
136
+ const type = escapeHtml(this.getAttribute("type") ?? "button");
137
+ const hrefValue = href ? escapeHtml(href) : "";
138
+ const target = this.getAttribute("target");
139
+ const safeTarget = target ? escapeHtml(target) : "";
140
+
141
+ this.shadow.innerHTML = `
142
+ <${tag}
143
+ class="${classes}"
144
+ part="control"
145
+ ${isLink ? `href="${hrefValue}"` : `type="${type}"`}
146
+ ${isLink && safeTarget ? `target="${safeTarget}"` : ""}
147
+ ${isLink && target === "_blank" ? 'rel="noopener noreferrer"' : ""}
148
+ ${this.disabled || this.loading ? "disabled" : ""}
149
+ ${label ? `aria-label="${escapeHtml(label)}"` : ""}
150
+ ${busy ? `aria-busy="${busy}"` : ""}
151
+ ${this.disabled && isLink ? 'aria-disabled="true" tabindex="-1"' : ""}
152
+ >
153
+ ${this.loading ? '<span class="viking-btn-spinner" aria-hidden="true"></span>' : ""}
154
+ <span class="viking-btn-label" part="label"><slot></slot></span>
155
+ </${tag}>
156
+ `;
157
+
158
+ this.control = this.shadow.querySelector(tag);
159
+ }
160
+ }
161
+
162
+ export const registerVikingButtonWc = (): void => {
163
+ defineCustomElement(VikingButtonWc.tag, VikingButtonWc);
164
+ defineCustomElementAlias(VikingButtonWc.legacyTag, VikingButtonWc);
165
+ };
@@ -0,0 +1,105 @@
1
+ import { attachShadowStyles } from "../core/base";
2
+ import {
3
+ defineCustomElement,
4
+ defineCustomElementAlias,
5
+ escapeHtml,
6
+ HTMLElementBase,
7
+ } from "../core/dom";
8
+ import { renderInlineIcon, TONE_ICON_NAMES } from "../core/icons-inline";
9
+ import { VIKING_CALLOUT_STYLES } from "../core/styles";
10
+ import type { VikingWcTone } from "../core/types";
11
+
12
+ const TONES = new Set<VikingWcTone>([
13
+ "accent",
14
+ "secondary",
15
+ "success",
16
+ "warning",
17
+ "danger",
18
+ "info",
19
+ "muted",
20
+ ]);
21
+
22
+ /**
23
+ * Framework-agnostic Viking callout Web Component.
24
+ * Tag: `viking-callout` (legacy alias: `viking-callout-wc`)
25
+ *
26
+ * @attr tone - Semantic tone (default: info)
27
+ * @attr heading - Bold callout title
28
+ * @attr icon - Override leading icon from the Viking registry
29
+ * @attr dismissible - Shows dismiss control; dispatches `viking-close`
30
+ *
31
+ * @example
32
+ * <viking-callout-wc tone="warning" heading="Degraded worker">Lag exceeds 3s.</viking-callout-wc>
33
+ */
34
+ export class VikingCalloutWc extends HTMLElementBase {
35
+ static readonly tag = "viking-callout";
36
+ static readonly legacyTag = "viking-callout-wc";
37
+
38
+ static get observedAttributes(): string[] {
39
+ return ["tone", "heading", "icon", "dismissible", "hidden"];
40
+ }
41
+
42
+ private readonly shadow: ShadowRoot;
43
+
44
+ constructor() {
45
+ super();
46
+ this.shadow = this.attachShadow({ mode: "open" });
47
+ attachShadowStyles(this.shadow, VIKING_CALLOUT_STYLES);
48
+ }
49
+
50
+ connectedCallback(): void {
51
+ this.render();
52
+ }
53
+
54
+ attributeChangedCallback(): void {
55
+ if (this.isConnected) {
56
+ this.render();
57
+ }
58
+ }
59
+
60
+ private get tone(): VikingWcTone {
61
+ const value = (this.getAttribute("tone") ?? "info") as VikingWcTone;
62
+ return TONES.has(value) ? value : "info";
63
+ }
64
+
65
+ private get dismissible(): boolean {
66
+ return (
67
+ this.hasAttribute("dismissible") &&
68
+ this.getAttribute("dismissible") !== "false"
69
+ );
70
+ }
71
+
72
+ private readonly onDismiss = (): void => {
73
+ this.setAttribute("hidden", "");
74
+ this.dispatchEvent(
75
+ new CustomEvent("viking-close", { bubbles: true, composed: true }),
76
+ );
77
+ };
78
+
79
+ private render(): void {
80
+ const heading = this.getAttribute("heading") ?? "";
81
+ const iconName =
82
+ this.getAttribute("icon") ?? TONE_ICON_NAMES[this.tone] ?? "info";
83
+ const iconMarkup = renderInlineIcon(iconName, 22, "viking-callout-icon");
84
+
85
+ this.shadow.innerHTML = `
86
+ <div class="viking-callout viking-callout-${this.tone}" role="note" part="surface">
87
+ <span part="icon">${iconMarkup}</span>
88
+ <div class="viking-callout-body" part="body">
89
+ ${heading ? `<p class="viking-callout-heading" part="heading">${escapeHtml(heading)}</p>` : ""}
90
+ <div class="viking-callout-text" part="text"><slot></slot></div>
91
+ </div>
92
+ ${this.dismissible ? `<button type="button" class="viking-callout-close" part="close" aria-label="Dismiss">${renderInlineIcon("x", 18)}</button>` : ""}
93
+ </div>
94
+ `;
95
+
96
+ this.shadow
97
+ .querySelector(".viking-callout-close")
98
+ ?.addEventListener("click", this.onDismiss);
99
+ }
100
+ }
101
+
102
+ export const registerVikingCalloutWc = (): void => {
103
+ defineCustomElement(VikingCalloutWc.tag, VikingCalloutWc);
104
+ defineCustomElementAlias(VikingCalloutWc.legacyTag, VikingCalloutWc);
105
+ };