@luisrodonccs/simplest-ui 0.0.0-stage → 0.1.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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -3
  3. package/components.manifest.json +54 -0
  4. package/dist/accordion.js +96 -0
  5. package/dist/base.css +1 -0
  6. package/dist/chunks/a11y-BYNN9rH7.js +59 -0
  7. package/dist/chunks/position-BdzQ8shu.js +7 -0
  8. package/dist/components/accordion.css +1 -0
  9. package/dist/components/accordion.d.ts +29 -0
  10. package/dist/components/accordion.d.ts.map +1 -0
  11. package/dist/components/alert.css +1 -0
  12. package/dist/components/avatar.css +1 -0
  13. package/dist/components/badge.css +1 -0
  14. package/dist/components/button.css +1 -0
  15. package/dist/components/card.css +1 -0
  16. package/dist/components/checkbox.css +1 -0
  17. package/dist/components/drawer.d.ts +38 -0
  18. package/dist/components/drawer.d.ts.map +1 -0
  19. package/dist/components/dropdown.d.ts +39 -0
  20. package/dist/components/dropdown.d.ts.map +1 -0
  21. package/dist/components/empty-state.css +1 -0
  22. package/dist/components/index.d.ts +17 -0
  23. package/dist/components/index.d.ts.map +1 -0
  24. package/dist/components/input.css +1 -0
  25. package/dist/components/internal/a11y.d.ts +20 -0
  26. package/dist/components/internal/a11y.d.ts.map +1 -0
  27. package/dist/components/internal/position.d.ts +12 -0
  28. package/dist/components/internal/position.d.ts.map +1 -0
  29. package/dist/components/modal.d.ts +40 -0
  30. package/dist/components/modal.d.ts.map +1 -0
  31. package/dist/components/nav.css +1 -0
  32. package/dist/components/popover.css +1 -0
  33. package/dist/components/popover.d.ts +30 -0
  34. package/dist/components/popover.d.ts.map +1 -0
  35. package/dist/components/progress.css +1 -0
  36. package/dist/components/radio.css +1 -0
  37. package/dist/components/select.css +1 -0
  38. package/dist/components/skeleton.css +1 -0
  39. package/dist/components/spinner.css +1 -0
  40. package/dist/components/stat.css +1 -0
  41. package/dist/components/switch.css +1 -0
  42. package/dist/components/table.css +1 -0
  43. package/dist/components/tabs.d.ts +30 -0
  44. package/dist/components/tabs.d.ts.map +1 -0
  45. package/dist/components/textarea.css +1 -0
  46. package/dist/components/toast.css +1 -0
  47. package/dist/components/toast.d.ts +31 -0
  48. package/dist/components/toast.d.ts.map +1 -0
  49. package/dist/components/tooltip.css +1 -0
  50. package/dist/components/tooltip.d.ts +15 -0
  51. package/dist/components/tooltip.d.ts.map +1 -0
  52. package/dist/components/topbar.css +1 -0
  53. package/dist/components.css +1 -0
  54. package/dist/components.js +19 -0
  55. package/dist/drawer.js +182 -0
  56. package/dist/dropdown.js +278 -0
  57. package/dist/index.d.ts +10 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +19 -0
  60. package/dist/modal.js +199 -0
  61. package/dist/popover.js +83 -0
  62. package/dist/reset.css +1 -0
  63. package/dist/styles.css +1 -0
  64. package/dist/tabs.js +253 -0
  65. package/dist/toast.js +61 -0
  66. package/dist/tokens.css +1 -0
  67. package/dist/tooltip.js +27 -0
  68. package/package.json +138 -6
  69. package/skills/simplest-ui/INSTALL.md +46 -0
  70. package/skills/simplest-ui/SKILL.md +62 -0
  71. package/skills/simplest-ui/evals/README.md +20 -0
  72. package/skills/simplest-ui/evals/RESULTS.md +23 -0
  73. package/skills/simplest-ui/evals/evals.json +23 -0
  74. package/skills/simplest-ui/references/authoring.md +18 -0
  75. package/skills/simplest-ui/references/catalog.md +49 -0
package/dist/tabs.js ADDED
@@ -0,0 +1,253 @@
1
+ import { css as h, LitElement as p, html as u } from "lit";
2
+ import { property as b, state as v, customElement as f } from "lit/decorators.js";
3
+ var g = Object.defineProperty, m = Object.getOwnPropertyDescriptor, d = (t, e, s, a) => {
4
+ for (var i = a > 1 ? void 0 : a ? m(e, s) : e, r = t.length - 1, o; r >= 0; r--)
5
+ (o = t[r]) && (i = (a ? o(e, s, i) : o(i)) || i);
6
+ return a && i && g(e, s, i), i;
7
+ };
8
+ let l = class extends p {
9
+ constructor() {
10
+ super(...arguments), this.uid = ++l.instanceCount, this.value = "", this.variant = "pill", this.tabs = [], this.lastSignature = "", this.labelObserver = new MutationObserver(() => this.collectTabs()), this.handleSlotChange = () => {
11
+ this.collectTabs();
12
+ }, this.handleKeydown = (t) => {
13
+ const e = this.tabs.map((n, c) => n.disabled ? -1 : c).filter((n) => n >= 0);
14
+ if (!e.length) return;
15
+ const s = Math.max(this.selectedIndex, 0), a = e.indexOf(s), i = a >= 0 ? a : 0;
16
+ let r = i;
17
+ switch (t.key) {
18
+ case "ArrowRight":
19
+ r = (i + 1) % e.length;
20
+ break;
21
+ case "ArrowLeft":
22
+ r = (i - 1 + e.length) % e.length;
23
+ break;
24
+ case "Home":
25
+ r = 0;
26
+ break;
27
+ case "End":
28
+ r = e.length - 1;
29
+ break;
30
+ default:
31
+ return;
32
+ }
33
+ t.preventDefault();
34
+ const o = e[r];
35
+ this.select(this.tabs[o].id), this.updateComplete.then(() => {
36
+ var n;
37
+ (n = this.renderRoot.querySelectorAll(".tabs-pill")[o]) == null || n.focus();
38
+ });
39
+ };
40
+ }
41
+ get selectedIndex() {
42
+ return this.tabs.findIndex((t) => t.id === this.value);
43
+ }
44
+ connectedCallback() {
45
+ super.connectedCallback(), this.labelObserver.observe(this, { attributes: !0, attributeFilter: ["label"], subtree: !0 }), this.collectTabs(), queueMicrotask(() => {
46
+ this.isConnected && this.collectTabs();
47
+ });
48
+ }
49
+ disconnectedCallback() {
50
+ this.labelObserver.disconnect(), super.disconnectedCallback();
51
+ }
52
+ willUpdate(t) {
53
+ if (!t.has("value")) return;
54
+ const e = this.tabs.filter((a) => !a.disabled);
55
+ if (!e.length) return;
56
+ const s = this.tabs.find((a) => a.id === this.value);
57
+ if (!s || s.disabled) {
58
+ this.value = e[0].id;
59
+ return;
60
+ }
61
+ this.tabs.some((a) => a.id === this.value) || (this.value = e[0].id);
62
+ }
63
+ updated(t) {
64
+ t.has("value") && this.applySelection();
65
+ }
66
+ collectTabs() {
67
+ var s;
68
+ const t = Array.from(this.children).filter(
69
+ (a) => a instanceof HTMLElement && a.hasAttribute("data-tab")
70
+ ), e = t.map((a) => `${a.getAttribute("data-tab")}|${a.getAttribute("label")}|${a.hasAttribute("disabled")}`).join("~");
71
+ if (e === this.lastSignature) {
72
+ this.applySelection();
73
+ return;
74
+ }
75
+ this.lastSignature = e, this.tabs = t.map((a, i) => {
76
+ const r = a.getAttribute("data-tab") ?? String(i);
77
+ return a.id || (a.id = `sui-tabpanel-${this.uid}-${i}`), a.setAttribute("role", "tabpanel"), {
78
+ id: r,
79
+ label: a.getAttribute("label") ?? r,
80
+ panel: a,
81
+ disabled: a.hasAttribute("disabled")
82
+ };
83
+ }), (!this.value || !this.tabs.some((a) => a.id === this.value)) && (this.value = ((s = this.tabs[0]) == null ? void 0 : s.id) ?? ""), this.applySelection(), this.setAttribute("data-ready", "");
84
+ }
85
+ applySelection() {
86
+ for (const t of this.tabs)
87
+ t.panel.hidden = t.id !== this.value;
88
+ }
89
+ select(t) {
90
+ const e = this.tabs.find((s) => s.id === t);
91
+ !e || e.disabled || this.value === t || (this.value = t, this.dispatchEvent(
92
+ new CustomEvent("sui-change", { detail: { value: t }, bubbles: !0, composed: !0 })
93
+ ));
94
+ }
95
+ tabId(t) {
96
+ return `sui-tab-${this.uid}-${t}`;
97
+ }
98
+ render() {
99
+ return u`
100
+ <div
101
+ class="list"
102
+ role="tablist"
103
+ aria-orientation="horizontal"
104
+ @keydown=${this.handleKeydown}
105
+ >
106
+ ${this.tabs.map((t, e) => {
107
+ const s = t.id === this.value;
108
+ return u`
109
+ <button
110
+ class="tabs-pill"
111
+ type="button"
112
+ role="tab"
113
+ id=${this.tabId(e)}
114
+ aria-selected=${s ? "true" : "false"}
115
+ aria-disabled=${t.disabled ? "true" : "false"}
116
+ ?disabled=${t.disabled}
117
+ tabindex=${s && !t.disabled ? 0 : -1}
118
+ @click=${() => this.select(t.id)}
119
+ >${t.label}</button>
120
+ `;
121
+ })}
122
+ </div>
123
+ <slot @slotchange=${this.handleSlotChange}></slot>
124
+ `;
125
+ }
126
+ };
127
+ l.instanceCount = 0;
128
+ l.styles = h`
129
+ :host {
130
+ display: block;
131
+ }
132
+ .list {
133
+ display: flex;
134
+ gap: var(--sui-space-1);
135
+ background: var(--sui-color-surface-raised);
136
+ border: 1px solid var(--sui-color-border);
137
+ border-radius: var(--sui-radius-full);
138
+ padding: var(--sui-space-1);
139
+ width: fit-content;
140
+ max-width: 100%;
141
+ overflow-x: auto;
142
+ scrollbar-width: none;
143
+ backdrop-filter: blur(var(--sui-blur-sm));
144
+ -webkit-backdrop-filter: blur(var(--sui-blur-sm));
145
+ }
146
+ .list::-webkit-scrollbar {
147
+ display: none;
148
+ }
149
+ .tabs-pill {
150
+ padding: var(--sui-space-1-5) var(--sui-space-3-5);
151
+ font-size: var(--sui-font-size-xs);
152
+ font-weight: var(--sui-font-weight-medium);
153
+ color: var(--sui-color-text-muted);
154
+ background: transparent;
155
+ border: 0;
156
+ border-radius: var(--sui-radius-full);
157
+ cursor: pointer;
158
+ transition: background var(--sui-duration-fast) var(--sui-easing-standard),
159
+ color var(--sui-duration-fast) var(--sui-easing-standard),
160
+ filter var(--sui-duration-fast) var(--sui-easing-standard);
161
+ }
162
+ .tabs-pill:hover {
163
+ color: var(--sui-color-text);
164
+ }
165
+ .tabs-pill[aria-selected="true"] {
166
+ background: var(--sui-gradient-primary);
167
+ color: var(--sui-color-primary-contrast);
168
+ }
169
+ .tabs-pill[aria-selected="true"]:hover {
170
+ background: var(--sui-gradient-primary-hover);
171
+ }
172
+ .tabs-pill:focus-visible {
173
+ outline: none;
174
+ box-shadow: var(--sui-focus-ring);
175
+ }
176
+ .tabs-pill:disabled,
177
+ .tabs-pill[aria-disabled="true"] {
178
+ opacity: 0.5;
179
+ cursor: not-allowed;
180
+ }
181
+
182
+ :host([variant="box"]) .list {
183
+ background: var(--sui-color-surface-muted);
184
+ border: 1px solid var(--sui-color-border);
185
+ border-radius: var(--sui-radius-lg);
186
+ padding: var(--sui-space-0-5);
187
+ gap: var(--sui-space-0-5);
188
+ width: fit-content;
189
+ }
190
+
191
+ :host([variant="box"]) .tabs-pill {
192
+ min-height: 2rem;
193
+ padding: 0 var(--sui-space-2);
194
+ color: var(--sui-color-text-muted);
195
+ background: transparent;
196
+ border: 0;
197
+ border-radius: var(--sui-radius-md);
198
+ font-size: var(--sui-font-size-xs);
199
+ }
200
+
201
+ :host([variant="box"]) .tabs-pill:hover {
202
+ color: var(--sui-color-text);
203
+ }
204
+
205
+ :host([variant="box"]) .tabs-pill[aria-selected="true"] {
206
+ color: var(--sui-color-primary-text);
207
+ background: var(--sui-color-surface-raised);
208
+ box-shadow: var(--sui-shadow-sm);
209
+ }
210
+
211
+ :host([variant="box"]) .tabs-pill[aria-selected="true"]:hover {
212
+ background: var(--sui-color-surface-raised);
213
+ }
214
+
215
+ :host([variant="box"]) .tabs-pill:disabled,
216
+ :host([variant="box"]) .tabs-pill[aria-disabled="true"] {
217
+ opacity: 0.5;
218
+ cursor: not-allowed;
219
+ }
220
+
221
+ :host([variant="box"]) ::slotted([data-tab]) {
222
+ display: none;
223
+ margin: 0;
224
+ }
225
+ :host(:not([data-ready])) ::slotted([data-tab]) {
226
+ display: none;
227
+ }
228
+ ::slotted([data-tab]) {
229
+ display: block;
230
+ margin-top: var(--sui-space-3);
231
+ padding: var(--sui-space-1) var(--sui-space-0-5);
232
+ font-size: var(--sui-font-size-sm);
233
+ color: var(--sui-color-text-muted);
234
+ }
235
+ ::slotted([data-tab][hidden]) {
236
+ display: none;
237
+ }
238
+ `;
239
+ d([
240
+ b({ type: String })
241
+ ], l.prototype, "value", 2);
242
+ d([
243
+ b({ type: String })
244
+ ], l.prototype, "variant", 2);
245
+ d([
246
+ v()
247
+ ], l.prototype, "tabs", 2);
248
+ l = d([
249
+ f("sui-tabs")
250
+ ], l);
251
+ export {
252
+ l as SuiTabs
253
+ };
package/dist/toast.js ADDED
@@ -0,0 +1,61 @@
1
+ class r extends HTMLElement {
2
+ constructor() {
3
+ super(...arguments), this.generated = !1;
4
+ }
5
+ static get observedAttributes() {
6
+ return ["title", "message", "variant", "duration"];
7
+ }
8
+ get duration() {
9
+ const t = Number(this.getAttribute("duration") ?? 5e3);
10
+ return Number.isFinite(t) ? Math.max(0, t) : 5e3;
11
+ }
12
+ connectedCallback() {
13
+ this.classList.add("ui-toast"), this.setAttribute("role", this.getAttribute("role") ?? "status"), this.setAttribute("aria-live", this.getAttribute("aria-live") ?? "polite"), this.render(), this.scheduleDismiss();
14
+ }
15
+ disconnectedCallback() {
16
+ window.clearTimeout(this.dismissTimer);
17
+ }
18
+ attributeChangedCallback() {
19
+ this.isConnected && (this.render(), this.scheduleDismiss());
20
+ }
21
+ dismiss() {
22
+ window.clearTimeout(this.dismissTimer), this.dispatchEvent(new CustomEvent("sui-dismiss", { bubbles: !0, composed: !0 })), this.remove();
23
+ }
24
+ render() {
25
+ const t = this.getAttribute("variant") ?? "default";
26
+ if (this.className = `ui-toast${t !== "default" ? ` ui-toast--${t}` : ""}`, !this.getAttribute("title")) {
27
+ this.generated && this.replaceChildren();
28
+ return;
29
+ }
30
+ if (this.children.length && !this.generated) return;
31
+ this.generated && this.replaceChildren();
32
+ const i = document.createElement("div"), e = document.createElement("div");
33
+ e.className = "ui-toast__title", e.textContent = this.getAttribute("title"), i.append(e), this.generated = !0;
34
+ const a = this.getAttribute("message");
35
+ if (a) {
36
+ const s = document.createElement("div");
37
+ s.className = "ui-toast__body", s.textContent = a, i.append(s);
38
+ }
39
+ this.append(i);
40
+ }
41
+ scheduleDismiss() {
42
+ window.clearTimeout(this.dismissTimer), this.duration > 0 && (this.dismissTimer = window.setTimeout(() => this.dismiss(), this.duration));
43
+ }
44
+ static show(t) {
45
+ let i = document.querySelector("sui-toast-container");
46
+ i || (i = document.createElement("sui-toast-container"), document.body.append(i));
47
+ const e = document.createElement("sui-toast");
48
+ return e.setAttribute("title", t.title), t.message && e.setAttribute("message", t.message), t.variant && t.variant !== "default" && e.setAttribute("variant", t.variant), t.duration !== void 0 && e.setAttribute("duration", String(t.duration)), i.append(e), e;
49
+ }
50
+ }
51
+ class u extends HTMLElement {
52
+ connectedCallback() {
53
+ this.classList.add("ui-toast-container"), this.setAttribute("aria-live", this.getAttribute("aria-live") ?? "polite"), this.setAttribute("aria-relevant", "additions");
54
+ }
55
+ }
56
+ customElements.get("sui-toast") || customElements.define("sui-toast", r);
57
+ customElements.get("sui-toast-container") || customElements.define("sui-toast-container", u);
58
+ export {
59
+ r as SuiToast,
60
+ u as SuiToastContainer
61
+ };
@@ -0,0 +1 @@
1
+ :root{color-scheme:light;--sui-color-primary: #6366f1;--sui-color-primary-hover: #4f46e5;--sui-color-primary-active: #4338ca;--sui-color-primary-text: #4338ca;--sui-color-primary-contrast: #ffffff;--sui-color-secondary: #ec4899;--sui-color-secondary-hover: #db2777;--sui-color-success: #059669;--sui-color-warning: #d97706;--sui-color-danger: #dc2626;--sui-color-info: #6366f1;--sui-color-success-surface: #ecfdf5;--sui-color-success-text: #065f46;--sui-color-success-border: #a7f3d0;--sui-color-warning-surface: #fffbeb;--sui-color-warning-text: #92400e;--sui-color-warning-border: #fde68a;--sui-color-danger-surface: #fef2f2;--sui-color-danger-surface-hover: #fee2e2;--sui-color-danger-text: #991b1b;--sui-color-danger-border: #fecaca;--sui-color-danger-ring: rgba(220, 38, 38, .12);--sui-color-info-surface: #eef2ff;--sui-color-info-text: #3730a3;--sui-color-info-border: rgba(99, 102, 241, .25);--sui-color-bg: #f5f6ff;--sui-gradient-page: linear-gradient(120deg, #eef2ff, #fce7f3);--sui-gradient-primary: linear-gradient(135deg, var(--sui-color-primary-hover), var(--sui-color-secondary-hover));--sui-gradient-primary-hover: linear-gradient(135deg, var(--sui-color-primary-active), #be185d);--sui-gradient-sidebar: linear-gradient(180deg, rgba(99, 102, 241, .06), rgba(236, 72, 153, .04));--sui-color-surface: rgba(255, 255, 255, .65);--sui-color-surface-muted: #f6f7fb;--sui-color-surface-raised: rgba(255, 255, 255, .85);--sui-color-text: #1f2937;--sui-color-text-muted: #6b7280;--sui-color-text-subtle: #9ca3af;--sui-color-border: rgba(99, 102, 241, .12);--sui-color-border-strong: rgba(99, 102, 241, .25);--sui-color-inverse-surface: #1f2937;--sui-color-inverse-text: #ffffff;--sui-color-control-knob: #ffffff;--sui-color-backdrop: rgb(31 41 55 / .35);--sui-color-focus: #6366f1;--sui-focus-ring: 0 0 0 3px rgba(99, 102, 241, .15);--sui-scrollbar-size: .65rem;--sui-scrollbar-track: transparent;--sui-scrollbar-thumb: var(--sui-color-border-strong);--sui-scrollbar-thumb-hover: var(--sui-color-text-subtle)}[data-theme=dark]{color-scheme:dark;--sui-color-primary: #818cf8;--sui-color-primary-hover: #6366f1;--sui-color-primary-active: #4f46e5;--sui-color-primary-text: #c7d2fe;--sui-color-primary-contrast: #ffffff;--sui-color-secondary: #f472b6;--sui-color-secondary-hover: #ec4899;--sui-color-success: #34d399;--sui-color-warning: #fbbf24;--sui-color-danger: #f87171;--sui-color-info: #818cf8;--sui-color-success-surface: rgba(5, 150, 105, .2);--sui-color-success-text: #a7f3d0;--sui-color-success-border: rgba(5, 150, 105, .35);--sui-color-warning-surface: rgba(217, 119, 6, .14);--sui-color-warning-text: #fcd34d;--sui-color-warning-border: rgba(217, 119, 6, .35);--sui-color-danger-surface: rgba(220, 38, 38, .2);--sui-color-danger-surface-hover: rgba(220, 38, 38, .28);--sui-color-danger-text: #fecaca;--sui-color-danger-border: rgba(220, 38, 38, .35);--sui-color-danger-ring: rgba(248, 113, 113, .2);--sui-color-info-surface: rgba(99, 102, 241, .22);--sui-color-info-text: #e0e7ff;--sui-color-info-border: rgba(99, 102, 241, .4);--sui-color-bg: #0b1020;--sui-gradient-page: linear-gradient(120deg, #0f172a, #1e1b4b 60%, #3b0d2a);--sui-gradient-primary: linear-gradient(135deg, var(--sui-color-primary), var(--sui-color-secondary));--sui-gradient-primary-hover: linear-gradient(135deg, #a5b4fc, #f9a8d4);--sui-gradient-sidebar: linear-gradient(180deg, rgba(129, 140, 248, .08), rgba(244, 114, 182, .05));--sui-color-surface: rgba(17, 24, 39, .6);--sui-color-surface-muted: #111827;--sui-color-surface-raised: rgba(31, 41, 55, .7);--sui-color-text: #f9fafb;--sui-color-text-muted: #d1d5db;--sui-color-text-subtle: #9ca3af;--sui-color-border: rgba(99, 102, 241, .2);--sui-color-border-strong: rgba(99, 102, 241, .35);--sui-color-inverse-surface: #f9fafb;--sui-color-inverse-text: #111827;--sui-color-control-knob: #ffffff;--sui-color-backdrop: rgb(0 0 0 / .5);--sui-color-focus: #818cf8;--sui-focus-ring: 0 0 0 3px rgba(129, 140, 248, .2);--sui-scrollbar-thumb: var(--sui-color-border-strong);--sui-scrollbar-thumb-hover: var(--sui-color-text-muted)}:root{--sui-space-0: 0;--sui-space-0-5: .125rem;--sui-space-1: .25rem;--sui-space-1-5: .375rem;--sui-space-2: .5rem;--sui-space-2-5: .625rem;--sui-space-3: .75rem;--sui-space-3-5: .875rem;--sui-space-4: 1rem;--sui-space-4-5: 1.125rem;--sui-space-5: 1.25rem;--sui-space-5-5: 1.375rem;--sui-space-6: 1.5rem;--sui-space-8: 2rem;--sui-space-10: 2.5rem;--sui-space-12: 3rem;--sui-space-16: 4rem;--sui-sidebar-width: 240px;--sui-control-height-sm: 28px;--sui-control-height-md: 36px;--sui-field-height: 38px;--sui-check-size: 16px;--sui-container-sm: 640px;--sui-container-md: 768px;--sui-container-lg: 1024px;--sui-container-xl: 1280px}:root{--sui-font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;--sui-font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;--sui-font-size-3xs: .625rem;--sui-font-size-2xs: .6875rem;--sui-font-size-xs: .75rem;--sui-font-size-sm: .8125rem;--sui-font-size-md: .875rem;--sui-font-size-base: 1rem;--sui-font-size-lg: 1.125rem;--sui-font-size-xl: 1.25rem;--sui-font-size-2xl: 1.5rem;--sui-font-size-3xl: 1.875rem;--sui-font-weight-normal: 400;--sui-font-weight-medium: 500;--sui-font-weight-semibold: 600;--sui-font-weight-bold: 700;--sui-line-height-tight: 1.25;--sui-line-height-normal: 1.5;--sui-line-height-relaxed: 1.75;--sui-letter-spacing-tight: -.02em;--sui-letter-spacing-normal: 0;--sui-letter-spacing-wide: .04em}:root{--sui-radius-none: 0;--sui-radius-xs: .375rem;--sui-radius-sm: .5rem;--sui-radius-md: .625rem;--sui-radius-lg: .75rem;--sui-radius-xl: .875rem;--sui-radius-2xl: 1rem;--sui-radius-full: 999px}:root{--sui-shadow-xs: 0 1px 2px 0 rgb(0 0 0 / .05);--sui-shadow-sm: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--sui-shadow-md: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--sui-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--sui-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--sui-shadow-card: 0 8px 30px rgba(99, 102, 241, .08);--sui-shadow-card-hover: 0 12px 40px rgba(99, 102, 241, .12);--sui-shadow-floating: 0 8px 30px rgba(99, 102, 241, .12);--sui-shadow-menu: 0 10px 30px rgba(99, 102, 241, .12);--sui-shadow-modal: 0 20px 60px rgba(99, 102, 241, .18);--sui-shadow-drawer: -8px 0 30px rgba(99, 102, 241, .12)}[data-theme=dark]{--sui-shadow-card: 0 8px 30px rgb(0 0 0 / .35);--sui-shadow-card-hover: 0 12px 40px rgb(0 0 0 / .45);--sui-shadow-floating: 0 8px 30px rgb(0 0 0 / .45);--sui-shadow-menu: 0 10px 30px rgb(0 0 0 / .5);--sui-shadow-modal: 0 20px 60px rgb(0 0 0 / .55);--sui-shadow-drawer: -8px 0 30px rgb(0 0 0 / .5)}:root{--sui-duration-fast: .12s;--sui-duration-normal: .2s;--sui-duration-slow: .32s;--sui-duration-spinner: .7s;--sui-duration-shimmer: 1.4s;--sui-duration-progress: 1.2s;--sui-easing-standard: cubic-bezier(.2, 0, 0, 1);--sui-easing-emphasized: cubic-bezier(.2, 0, 0, 1.2)}@media (prefers-reduced-motion: reduce){:root{--sui-duration-fast: 0ms;--sui-duration-normal: 0ms;--sui-duration-slow: 0ms;--sui-duration-spinner: 0ms;--sui-duration-shimmer: 0ms;--sui-duration-progress: 0ms}}:root{--sui-blur-sm: 8px;--sui-blur-md: 12px;--sui-blur-lg: 16px;--sui-blur-backdrop: 6px;--sui-z-popover: 50;--sui-z-dropdown: 100;--sui-z-drawer-backdrop: 60;--sui-z-drawer: 61;--sui-z-modal: 1000;--sui-z-toast: 1100}
@@ -0,0 +1,27 @@
1
+ const e = class e extends HTMLElement {
2
+ constructor() {
3
+ super(...arguments), this.uid = ++e.instanceCount;
4
+ }
5
+ static get observedAttributes() {
6
+ return ["content"];
7
+ }
8
+ connectedCallback() {
9
+ this.classList.add("ui-tooltip"), this.upgrade();
10
+ }
11
+ attributeChangedCallback() {
12
+ this.isConnected && this.upgrade();
13
+ }
14
+ upgrade() {
15
+ let t = this.querySelector("[slot='content'], .ui-tooltip__content");
16
+ if (!t && this.getAttribute("content") && (t = document.createElement("span"), t.textContent = this.getAttribute("content"), this.append(t)), !t) return;
17
+ t.classList.add("ui-tooltip__content"), t.setAttribute("role", "tooltip"), t.id || (t.id = `sui-tooltip-${this.uid}`);
18
+ const s = this.querySelector("[slot='trigger'], button, a, input, [tabindex]") ?? this;
19
+ s === this && !this.hasAttribute("tabindex") && (this.tabIndex = 0), s.setAttribute("aria-describedby", t.id);
20
+ }
21
+ };
22
+ e.instanceCount = 0;
23
+ let i = e;
24
+ customElements.get("sui-tooltip") || customElements.define("sui-tooltip", i);
25
+ export {
26
+ i as SuiTooltip
27
+ };
package/package.json CHANGED
@@ -1,6 +1,138 @@
1
- {
2
- "name": "@luisrodonccs/simplest-ui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "@luisrodonccs/simplest-ui",
3
+ "version": "0.1.0",
4
+ "description": "Framework-agnostic design system with CSS tokens, layouts, and accessible Web Components.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./tokens.css": "./dist/tokens.css",
15
+ "./reset.css": "./dist/reset.css",
16
+ "./base.css": "./dist/base.css",
17
+ "./components.css": "./dist/components.css",
18
+ "./styles.css": "./dist/styles.css",
19
+ "./components/accordion.css": "./dist/components/accordion.css",
20
+ "./components/alert.css": "./dist/components/alert.css",
21
+ "./components/avatar.css": "./dist/components/avatar.css",
22
+ "./components/badge.css": "./dist/components/badge.css",
23
+ "./components/button.css": "./dist/components/button.css",
24
+ "./components/card.css": "./dist/components/card.css",
25
+ "./components/checkbox.css": "./dist/components/checkbox.css",
26
+ "./components/empty-state.css": "./dist/components/empty-state.css",
27
+ "./components/input.css": "./dist/components/input.css",
28
+ "./components/nav.css": "./dist/components/nav.css",
29
+ "./components/popover.css": "./dist/components/popover.css",
30
+ "./components/progress.css": "./dist/components/progress.css",
31
+ "./components/radio.css": "./dist/components/radio.css",
32
+ "./components/select.css": "./dist/components/select.css",
33
+ "./components/skeleton.css": "./dist/components/skeleton.css",
34
+ "./components/spinner.css": "./dist/components/spinner.css",
35
+ "./components/stat.css": "./dist/components/stat.css",
36
+ "./components/switch.css": "./dist/components/switch.css",
37
+ "./components/table.css": "./dist/components/table.css",
38
+ "./components/textarea.css": "./dist/components/textarea.css",
39
+ "./components/toast.css": "./dist/components/toast.css",
40
+ "./components/tooltip.css": "./dist/components/tooltip.css",
41
+ "./components/topbar.css": "./dist/components/topbar.css",
42
+ "./components.js": {
43
+ "types": "./dist/components/index.d.ts",
44
+ "import": "./dist/components.js"
45
+ },
46
+ "./components/accordion": {
47
+ "types": "./dist/components/accordion.d.ts",
48
+ "import": "./dist/accordion.js"
49
+ },
50
+ "./components/dropdown": {
51
+ "types": "./dist/components/dropdown.d.ts",
52
+ "import": "./dist/dropdown.js"
53
+ },
54
+ "./components/drawer": {
55
+ "types": "./dist/components/drawer.d.ts",
56
+ "import": "./dist/drawer.js"
57
+ },
58
+ "./components/modal": {
59
+ "types": "./dist/components/modal.d.ts",
60
+ "import": "./dist/modal.js"
61
+ },
62
+ "./components/popover": {
63
+ "types": "./dist/components/popover.d.ts",
64
+ "import": "./dist/popover.js"
65
+ },
66
+ "./components/tabs": {
67
+ "types": "./dist/components/tabs.d.ts",
68
+ "import": "./dist/tabs.js"
69
+ },
70
+ "./components/toast": {
71
+ "types": "./dist/components/toast.d.ts",
72
+ "import": "./dist/toast.js"
73
+ },
74
+ "./components/tooltip": {
75
+ "types": "./dist/components/tooltip.d.ts",
76
+ "import": "./dist/tooltip.js"
77
+ },
78
+ "./package.json": "./package.json"
79
+ },
80
+ "files": [
81
+ "dist",
82
+ "skills",
83
+ "LICENSE",
84
+ "components.manifest.json"
85
+ ],
86
+ "scripts": {
87
+ "dev": "vite --config vite.docs.config.ts --open /docs/",
88
+ "build": "vite build && tsc --emitDeclarationOnly && node scripts/verify-package.mjs",
89
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.docs.json && tsc --noEmit -p tsconfig.e2e.json",
90
+ "preview": "vite preview",
91
+ "test": "vitest run",
92
+ "test:watch": "vitest",
93
+ "test:e2e": "npm run build && playwright test",
94
+ "test:e2e:ui": "playwright test --ui",
95
+ "test:e2e:install": "playwright install chromium firefox webkit",
96
+ "test:visual": "playwright test --project=visual",
97
+ "test:visual:update": "playwright test --project=visual --update-snapshots",
98
+ "catalog:generate": "node scripts/generate-catalogs.mjs",
99
+ "catalog:check": "node scripts/check-catalogs.mjs",
100
+ "test:consumer": "vite build --config tests/fixtures/laravel/vite.config.ts && vite build --config tests/fixtures/ssr/vite.config.ts",
101
+ "docs:serve": "vite --config vite.docs.config.ts --host 127.0.0.1 --port 5173",
102
+ "docs:dev": "vite --config vite.docs.config.ts --open /docs/",
103
+ "docs:build": "npm run catalog:check && node scripts/verify-docs.mjs && vite build --config vite.docs.config.ts",
104
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
105
+ },
106
+ "dependencies": {
107
+ "lit": "^3.1.0"
108
+ },
109
+ "devDependencies": {
110
+ "@axe-core/playwright": "^4.14.0",
111
+ "@playwright/test": "^1.64.1",
112
+ "happy-dom": "^21.0.4",
113
+ "typescript": "^5.4.0",
114
+ "vite": "^5.2.0",
115
+ "vitest": "^1.6.0"
116
+ },
117
+ "keywords": [
118
+ "design-system",
119
+ "web-components",
120
+ "css",
121
+ "framework-agnostic"
122
+ ],
123
+ "license": "MIT",
124
+ "repository": {
125
+ "type": "git",
126
+ "url": "https://github.com/LuisRodOnCCS/simplest-ui.git"
127
+ },
128
+ "engines": {
129
+ "node": ">=18"
130
+ },
131
+ "publishConfig": {
132
+ "access": "public"
133
+ },
134
+ "homepage": "https://luisrodonccs.github.io/simplest-ui/",
135
+ "bugs": {
136
+ "url": "https://github.com/LuisRodOnCCS/simplest-ui/issues"
137
+ }
138
+ }
@@ -0,0 +1,46 @@
1
+ # Install the Simplest UI skill
2
+
3
+ The package ships the portable Agent Skill at `skills/simplest-ui`. Installing the npm package does not activate it automatically: copy it into an agent's skill directory, or configure the agent to load the package directory.
4
+
5
+ ## OpenCode V2
6
+
7
+ For a project-local setup, add the package skill directory to `opencode.jsonc`:
8
+
9
+ ```jsonc
10
+ {
11
+ "$schema": "https://opencode.ai/config.json",
12
+ "skills": ["./node_modules/@luisrodonccs/simplest-ui/skills"]
13
+ }
14
+ ```
15
+
16
+ OpenCode loads the nested `simplest-ui/SKILL.md` file from that directory. Start a new session or run `/reload`, then invoke it as `@simplest-ui` or let OpenCode select it for a relevant Simplest UI task.
17
+
18
+ To make a copied skill available in every project, copy `simplest-ui` to `~/.config/opencode/skills/`.
19
+
20
+ ## Claude Code
21
+
22
+ For a project-local setup, copy the skill folder into `.claude/skills/`.
23
+
24
+ **PowerShell**
25
+
26
+ ```powershell
27
+ New-Item -ItemType Directory -Force .claude/skills | Out-Null
28
+ Copy-Item -Recurse node_modules/@luisrodonccs/simplest-ui/skills/simplest-ui .claude/skills/simplest-ui
29
+ ```
30
+
31
+ **macOS or Linux**
32
+
33
+ ```sh
34
+ mkdir -p .claude/skills
35
+ cp -R node_modules/@luisrodonccs/simplest-ui/skills/simplest-ui .claude/skills/simplest-ui
36
+ ```
37
+
38
+ Restart the session or run `/reload-skills`, then invoke `/simplest-ui`. For a personal installation, use `~/.claude/skills/simplest-ui` instead.
39
+
40
+ ## Other Agent Skills clients
41
+
42
+ The skill follows the open [Agent Skills](https://agentskills.io) format. Copy the `simplest-ui` directory, including `SKILL.md` and `references/`, to the client-specific skills directory. Consult that client's documentation for its discovery path and reload command.
43
+
44
+ ## Verify the installation
45
+
46
+ Ask the agent to build a card with a status and action in a project using Simplest UI. It should select `ui-card`, `ui-badge`, and `ui-button`, inspect `components.manifest.json`, and avoid inventing a `<sui-card>` element.
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: simplest-ui
3
+ description: Build, extend, review, or document interfaces that use the Simplest UI design system. Use this skill whenever a task occurs in a project with @luisrodonccs/simplest-ui installed, imports ui-* classes or sui-* elements, or asks to add UI to Simplest UI. Check the existing catalog before creating markup or components, reuse the system first, and preserve its accessibility and token conventions.
4
+ ---
5
+
6
+ # Simplest UI
7
+
8
+ Build with Simplest UI as an existing product system, not as a bag of styles. The system is framework-agnostic: CSS classes provide presentational primitives, while only selected interactive patterns are registered Web Components.
9
+
10
+ ## Start with the catalog
11
+
12
+ 1. Locate `components.manifest.json` in the project root. If unavailable, read the package README and inspect the installed exports.
13
+ 2. Identify whether the requested UI is already available as:
14
+ - a CSS component (`ui-*` class),
15
+ - a layout primitive (`ui-stack`, `ui-grid`, `ui-app-shell`, etc.), or
16
+ - a registered Custom Element (`sui-*`).
17
+ 3. Reuse the closest existing primitive. Compose CSS components before proposing a new component.
18
+ 4. Never invent an unregistered `<sui-*>` tag. In particular, cards, tables and alerts are CSS components, not Custom Elements.
19
+
20
+ Read [references/catalog.md](references/catalog.md) for the current public surface and [references/authoring.md](references/authoring.md) before extending it.
21
+
22
+ ## Use the correct entrypoint
23
+
24
+ Use the full bundle when Simplest UI owns the page foundation:
25
+
26
+ ```ts
27
+ import "@luisrodonccs/simplest-ui/tokens.css";
28
+ import "@luisrodonccs/simplest-ui/styles.css";
29
+ ```
30
+
31
+ When Bootstrap, Tailwind, or another system owns global styles, omit the Simplest reset:
32
+
33
+ ```ts
34
+ import "@luisrodonccs/simplest-ui/tokens.css";
35
+ import "@luisrodonccs/simplest-ui/components.css";
36
+ ```
37
+
38
+ Register all Web Components with `components.js`, or import only the needed behavior from `components/<name>`.
39
+
40
+ ## Build markup
41
+
42
+ - Use `--sui-*` tokens and `ui-*` classes. Do not introduce parallel color, spacing, shadow or radius values when a token exists.
43
+ - Prefer semantic HTML: `<button>`, `<a>`, `<article>`, `<table>`, `<label>`, and form controls retain their native meaning.
44
+ - Use an interactive `sui-*` element only when it is listed in the catalog.
45
+ - Keep the component implementation as the behavior source of truth; examples and application code must consume it rather than recreating its focus, Escape, or ARIA behavior.
46
+ - Add a clear accessible name to controls and use appropriate live-region roles for feedback.
47
+
48
+ ## Extend only when necessary
49
+
50
+ Create a new CSS component if composition cannot communicate a reusable visual pattern. Create a Web Component only if the pattern has reusable state or complex accessible interaction.
51
+
52
+ When adding either kind:
53
+
54
+ 1. Reuse tokens and prefixes (`ui-`, `sui-`, `--sui-`).
55
+ 2. Implement keyboard support, focus behavior, semantic roles and `prefers-reduced-motion` where interaction requires them.
56
+ 3. Add unit tests for behavior; do not rely solely on a demo.
57
+ 4. Update `components.manifest.json`, the visual catalog, README, TypeScript JSX declarations when relevant, and changelog.
58
+ 5. Run `npm run typecheck`, `npm test`, `npm run build`, and `npm pack --dry-run`.
59
+
60
+ ## Output expectations
61
+
62
+ When responding to an implementation request, state which existing system primitive you selected and why. If no suitable primitive exists, briefly justify the extension before adding it. Mention any import requirement for new behavior.
@@ -0,0 +1,20 @@
1
+ # Skill evaluation protocol
2
+
3
+ The prompts in `evals.json` evaluate the three decisions the skill must make: compose existing primitives, select an existing interactive component, and justify a new component only when composition is insufficient.
4
+
5
+ ## Run an evaluation
6
+
7
+ 1. Use a clean fixture project with Simplest UI installed and `components.manifest.json` available.
8
+ 2. Run each prompt with the `simplest-ui` skill enabled and record the complete response and changed files.
9
+ 3. Review the response against its `expected_output` and the checks below.
10
+ 4. Repeat after material changes to `SKILL.md`, the manifest, or the public API.
11
+
12
+ ## Checks
13
+
14
+ - Existing UI requests select documented `ui-*` classes and registered `sui-*` elements.
15
+ - The response does not invent an unregistered Custom Element such as `sui-card`.
16
+ - Interactive patterns use the documented entrypoint rather than recreating keyboard or ARIA behavior.
17
+ - New component proposals explain why composition is insufficient and include tokens, accessibility, tests, manifest, visual catalog, and changelog updates.
18
+ - The response names the selected primitive and any required import.
19
+
20
+ Record the date, agent/version, fixture revision, prompt, output, and pass/fail result in a release or pull-request discussion, or in a versioned results file such as [RESULTS.md](./RESULTS.md). Do not mark the roadmap evaluation item complete until all three prompts have been reviewed successfully.
@@ -0,0 +1,23 @@
1
+ # OpenCode evaluation results
2
+
3
+ Evaluated on 2026-11-05 with OpenCode v2.2.5 and `openai/gpt-5.6-terra`.
4
+
5
+ ## Fixture
6
+
7
+ A clean temporary project installed the locally packed `@luisrodonccs/simplest-ui@0.1.0` tarball and loaded the package skill through `opencode.jsonc`:
8
+
9
+ ```jsonc
10
+ { "skills": ["./node_modules/@luisrodonccs/simplest-ui/skills"] }
11
+ ```
12
+
13
+ The evaluation prompts ran without changing fixture files. Each explicit invocation loaded `simplest-ui`; the export-menu prompt was also run without an explicit invocation and OpenCode selected the skill automatically.
14
+
15
+ ## Results
16
+
17
+ | ID | Scenario | Result | Evidence |
18
+ | --- | --- | --- | --- |
19
+ | 1 | Billing summary | Pass | Selected `ui-card`, `ui-stat`, `ui-badge`, and `ui-button`; used semantic `article`, `header`, and `footer`; proposed `styles.css`; did not invent `sui-card`. |
20
+ | 2 | Export actions | Pass | Selected `sui-dropdown`, imported `@luisrodonccs/simplest-ui/components/dropdown`, used its `sui-select` event, and delegated keyboard behavior to the component. Automatic skill selection also selected `simplest-ui`. |
21
+ | 3 | Date range picker | Pass | Determined that no date picker exists, justified a new `sui-date-range-picker`, and covered tokens, accessibility, keyboard behavior, tests, manifest generation, visual documentation, exports, and changelog work. |
22
+
23
+ All checks in [README.md](./README.md) passed. Re-run these prompts after material changes to the skill, manifest, or public API.