liqgui 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 (51) hide show
  1. package/README.md +190 -0
  2. package/dist/components/glass-accordion.d.ts +15 -0
  3. package/dist/components/glass-accordion.js +173 -0
  4. package/dist/components/glass-avatar.d.ts +9 -0
  5. package/dist/components/glass-avatar.js +98 -0
  6. package/dist/components/glass-badge.d.ts +10 -0
  7. package/dist/components/glass-badge.js +151 -0
  8. package/dist/components/glass-button.d.ts +6 -0
  9. package/dist/components/glass-button.js +124 -0
  10. package/dist/components/glass-card.d.ts +8 -0
  11. package/dist/components/glass-card.js +102 -0
  12. package/dist/components/glass-dropdown.d.ts +12 -0
  13. package/dist/components/glass-dropdown.js +182 -0
  14. package/dist/components/glass-input.d.ts +8 -0
  15. package/dist/components/glass-input.js +151 -0
  16. package/dist/components/glass-modal.d.ts +11 -0
  17. package/dist/components/glass-modal.js +128 -0
  18. package/dist/components/glass-navbar.d.ts +6 -0
  19. package/dist/components/glass-navbar.js +84 -0
  20. package/dist/components/glass-progress.d.ts +12 -0
  21. package/dist/components/glass-progress.js +159 -0
  22. package/dist/components/glass-slider.d.ts +17 -0
  23. package/dist/components/glass-slider.js +168 -0
  24. package/dist/components/glass-tabs.d.ts +7 -0
  25. package/dist/components/glass-tabs.js +102 -0
  26. package/dist/components/glass-toast.d.ts +8 -0
  27. package/dist/components/glass-toast.js +128 -0
  28. package/dist/components/glass-toggle.d.ts +9 -0
  29. package/dist/components/glass-toggle.js +112 -0
  30. package/dist/components/glass-tooltip.d.ts +14 -0
  31. package/dist/components/glass-tooltip.js +214 -0
  32. package/dist/core/base-element.d.ts +4 -0
  33. package/dist/core/base-element.js +9 -0
  34. package/dist/core/curves.d.ts +22 -0
  35. package/dist/core/curves.js +32 -0
  36. package/dist/core/focus-trap.d.ts +1 -0
  37. package/dist/core/focus-trap.js +19 -0
  38. package/dist/core/glow.d.ts +3 -0
  39. package/dist/core/glow.js +57 -0
  40. package/dist/core/motion.d.ts +12 -0
  41. package/dist/core/motion.js +54 -0
  42. package/dist/core/spring-engine.d.ts +12 -0
  43. package/dist/core/spring-engine.js +90 -0
  44. package/dist/core/supports.d.ts +1 -0
  45. package/dist/core/supports.js +1 -0
  46. package/dist/core/theme.d.ts +2 -0
  47. package/dist/core/theme.js +1 -0
  48. package/dist/index.d.ts +39 -0
  49. package/dist/index.js +40 -0
  50. package/package.json +47 -0
  51. package/src/styles/tokens.css +140 -0
package/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # liqgui ✨
2
+
3
+ [![npm version](https://img.shields.io/npm/v/liqgui.svg)](https://www.npmjs.com/package/liqgui)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Glassmorphism UI components that actually feel good. Built with spring physics.
7
+
8
+ **[📖 Docs & Demo](https://bymehul.github.io/liqgui/docs/index.html)**
9
+
10
+ ## Why this exists
11
+
12
+ Most glass-effect libraries look nice but feel off—stiff transitions, janky hovers, no thought put into motion. liqgui uses real spring physics (not just `ease-in-out`) so everything moves like it has weight.
13
+
14
+ ## What you get
15
+
16
+ - **15 components** — buttons, cards, modals, toasts, the works
17
+ - **Spring animations** — physics-based, not CSS keyframe guesswork
18
+ - **3D effects** — tilt on hover, ripples, glow trails
19
+ - **Dark/light themes** — one CSS variable swap
20
+ - **Accessible** — keyboard nav, focus traps, ARIA done right
21
+ - **No dependencies** — just TypeScript, works with anything
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install liqgui
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```html
32
+ <link rel="stylesheet" href="node_modules/liqgui/src/styles/tokens.css">
33
+ <script type="module">
34
+ import 'liqgui';
35
+ </script>
36
+
37
+ <glass-button variant="primary">Click Me</glass-button>
38
+ <glass-card glow>Your content here</glass-card>
39
+ ```
40
+
41
+ ## Components
42
+
43
+ ### Buttons & Actions
44
+ ```html
45
+ <glass-button>Default</glass-button>
46
+ <glass-button variant="primary">Primary</glass-button>
47
+ <glass-button variant="outline">Outline</glass-button>
48
+ <glass-button variant="ghost">Ghost</glass-button>
49
+ <glass-button loading>Loading...</glass-button>
50
+ ```
51
+
52
+ ### Form Controls
53
+ ```html
54
+ <glass-input placeholder="Enter text"></glass-input>
55
+ <glass-toggle checked>Enable feature</glass-toggle>
56
+ <glass-slider value="50" min="0" max="100"></glass-slider>
57
+ <glass-dropdown>
58
+ <span slot="trigger">Select option</span>
59
+ <button role="option" data-value="1">Option 1</button>
60
+ <button role="option" data-value="2">Option 2</button>
61
+ </glass-dropdown>
62
+ ```
63
+
64
+ ### Layout & Content
65
+ ```html
66
+ <glass-card glow>Card with glow effect</glass-card>
67
+ <glass-card no-tilt>Card without 3D tilt</glass-card>
68
+
69
+ <glass-tabs>
70
+ <button data-value="tab1">Tab 1</button>
71
+ <button data-value="tab2">Tab 2</button>
72
+ </glass-tabs>
73
+
74
+ <glass-navbar auto-hide>
75
+ <span slot="brand">Brand</span>
76
+ <a href="#">Link</a>
77
+ <glass-button slot="actions">CTA</glass-button>
78
+ </glass-navbar>
79
+ ```
80
+
81
+ ### Overlays
82
+ ```html
83
+ <glass-modal id="modal">Modal content</glass-modal>
84
+ <script>document.getElementById('modal').open();</script>
85
+
86
+ <glass-tooltip position="top">
87
+ <button>Hover me</button>
88
+ <span slot="content">Tooltip text</span>
89
+ </glass-tooltip>
90
+
91
+ <glass-popover position="bottom">
92
+ <button slot="trigger">Click me</button>
93
+ <div>Popover content</div>
94
+ </glass-popover>
95
+ ```
96
+
97
+ ### Notifications
98
+ ```javascript
99
+ import { GlassToast } from 'liqgui';
100
+ GlassToast.show('Success!', 'success');
101
+ GlassToast.show('Error!', 'error');
102
+ ```
103
+
104
+ ### Data Display
105
+ ```html
106
+ <glass-avatar size="lg" status="online">AB</glass-avatar>
107
+ <glass-avatar-group>
108
+ <glass-avatar>A</glass-avatar>
109
+ <glass-avatar>B</glass-avatar>
110
+ </glass-avatar-group>
111
+
112
+ <glass-badge variant="success">New</glass-badge>
113
+ <glass-badge-wrapper count="5">
114
+ <button>Notifications</button>
115
+ </glass-badge-wrapper>
116
+
117
+ <glass-progress value="75" max="100">75%</glass-progress>
118
+ <glass-progress variant="circular" value="50">50%</glass-progress>
119
+ <glass-progress indeterminate>Loading</glass-progress>
120
+ ```
121
+
122
+ ### Accordion
123
+ ```html
124
+ <glass-accordion>
125
+ <glass-accordion-item open>
126
+ <span slot="title">Question 1</span>
127
+ Answer content here.
128
+ </glass-accordion-item>
129
+ <glass-accordion-item>
130
+ <span slot="title">Question 2</span>
131
+ More content.
132
+ </glass-accordion-item>
133
+ </glass-accordion>
134
+ ```
135
+
136
+ ## Core Utilities
137
+
138
+ ```javascript
139
+ import {
140
+ springAnimate, bouncySpring,
141
+ motion,
142
+ setTheme
143
+ } from 'liqgui';
144
+
145
+ // Spring animation
146
+ springAnimate(0, 100, v => el.style.left = `${v}px`, bouncySpring);
147
+
148
+ // Motion library
149
+ motion.fadeIn(element);
150
+ motion.scaleIn(element);
151
+ motion.slideInUp(element);
152
+
153
+ // Theming
154
+ setTheme('dark');
155
+ ```
156
+
157
+ ## CSS Custom Properties
158
+
159
+ ```css
160
+ :root {
161
+ --lg-blur: 20px;
162
+ --lg-radius: 20px;
163
+ --lg-accent: var(--lg-accent-blue);
164
+ --lg-bg: var(--lg-bg-dark);
165
+ }
166
+ ```
167
+
168
+ ## Component List
169
+
170
+ | Category | Components |
171
+ |----------|-----------|
172
+ | Actions | `glass-button` |
173
+ | Forms | `glass-input`, `glass-toggle`, `glass-slider`, `glass-dropdown` |
174
+ | Layout | `glass-card`, `glass-tabs`, `glass-navbar`, `glass-accordion` |
175
+ | Overlays | `glass-modal`, `glass-tooltip`, `glass-popover`, `glass-toast` |
176
+ | Data | `glass-avatar`, `glass-badge`, `glass-progress` |
177
+
178
+ ## Browser Support
179
+
180
+ Modern browsers only (Chrome, Firefox, Safari, Edge). Uses Web Components and `backdrop-filter`.
181
+
182
+ ## Support
183
+
184
+ If you find this useful, consider supporting my work:
185
+
186
+ [![Support on Patreon](https://img.shields.io/badge/Patreon-Support-ff424d?logo=patreon)](https://www.patreon.com/c/bymehul) [![Buy me a Chai](https://img.shields.io/badge/Chai-Donate-ffdd00)](https://www.paywithchai.in/bymehul)
187
+
188
+ ## License
189
+
190
+ MIT — do whatever you want with it.
@@ -0,0 +1,15 @@
1
+ import { BaseElement } from "../core/base-element.js";
2
+ export declare class GlassAccordion extends BaseElement {
3
+ static get observedAttributes(): string[];
4
+ connectedCallback(): void;
5
+ }
6
+ export declare class GlassAccordionItem extends BaseElement {
7
+ private contentEl?;
8
+ private contentHeight;
9
+ static get observedAttributes(): string[];
10
+ connectedCallback(): void;
11
+ toggle(): void;
12
+ open(): void;
13
+ close(): void;
14
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
15
+ }
@@ -0,0 +1,173 @@
1
+ import { BaseElement } from "../core/base-element.js";
2
+ import { springAnimate, gentleSpring } from "../core/spring-engine.js";
3
+ export class GlassAccordion extends BaseElement {
4
+ static get observedAttributes() {
5
+ return ["multiple"];
6
+ }
7
+ connectedCallback() {
8
+ this.mount(`
9
+ <div class="accordion">
10
+ <slot></slot>
11
+ </div>
12
+ `, `
13
+ :host {
14
+ display: block;
15
+ }
16
+ .accordion {
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: 0.5rem;
20
+ }
21
+ `);
22
+ // Handle item toggle
23
+ this.addEventListener("accordion-toggle", ((e) => {
24
+ if (!this.hasAttribute("multiple")) {
25
+ // Close other items
26
+ this.querySelectorAll("glass-accordion-item[open]").forEach(item => {
27
+ if (item !== e.target) {
28
+ item.removeAttribute("open");
29
+ }
30
+ });
31
+ }
32
+ }));
33
+ }
34
+ }
35
+ customElements.define("glass-accordion", GlassAccordion);
36
+ export class GlassAccordionItem extends BaseElement {
37
+ constructor() {
38
+ super(...arguments);
39
+ this.contentHeight = 0;
40
+ }
41
+ static get observedAttributes() {
42
+ return ["open"];
43
+ }
44
+ connectedCallback() {
45
+ var _a;
46
+ this.mount(`
47
+ <div class="item">
48
+ <button class="header" aria-expanded="false">
49
+ <span class="title"><slot name="title"></slot></span>
50
+ <svg class="icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
51
+ <path d="M6 9l6 6 6-6"/>
52
+ </svg>
53
+ </button>
54
+ <div class="content-wrapper">
55
+ <div class="content">
56
+ <slot></slot>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ `, `
61
+ :host {
62
+ display: block;
63
+ }
64
+ .item {
65
+ background: var(--lg-bg);
66
+ backdrop-filter: blur(var(--lg-blur));
67
+ border: 1px solid var(--lg-border);
68
+ border-radius: var(--lg-radius);
69
+ overflow: hidden;
70
+ }
71
+ .header {
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: space-between;
75
+ width: 100%;
76
+ padding: 1rem 1.25rem;
77
+ background: transparent;
78
+ border: none;
79
+ color: inherit;
80
+ font: inherit;
81
+ font-weight: 500;
82
+ cursor: pointer;
83
+ text-align: left;
84
+ transition: background 0.2s ease;
85
+ }
86
+ .header:hover {
87
+ background: rgba(255, 255, 255, 0.05);
88
+ }
89
+ .header:focus-visible {
90
+ outline: 2px solid var(--lg-accent-focus);
91
+ outline-offset: -2px;
92
+ }
93
+ .icon {
94
+ transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
95
+ flex-shrink: 0;
96
+ opacity: 0.7;
97
+ }
98
+ :host([open]) .icon {
99
+ transform: rotate(180deg);
100
+ }
101
+ .content-wrapper {
102
+ height: 0;
103
+ overflow: hidden;
104
+ }
105
+ .content {
106
+ padding: 0 1.25rem 1rem;
107
+ opacity: 0.8;
108
+ line-height: 1.6;
109
+ }
110
+ `);
111
+ this.contentEl = this.root.querySelector(".content-wrapper");
112
+ // Get content height
113
+ requestAnimationFrame(() => {
114
+ const content = this.root.querySelector(".content");
115
+ if (content) {
116
+ this.contentHeight = content.offsetHeight;
117
+ }
118
+ if (this.hasAttribute("open")) {
119
+ this.contentEl.style.height = `${this.contentHeight}px`;
120
+ }
121
+ });
122
+ // Toggle on header click
123
+ (_a = this.root.querySelector(".header")) === null || _a === void 0 ? void 0 : _a.addEventListener("click", () => {
124
+ this.toggle();
125
+ });
126
+ }
127
+ toggle() {
128
+ if (this.hasAttribute("open")) {
129
+ this.close();
130
+ }
131
+ else {
132
+ this.open();
133
+ }
134
+ }
135
+ open() {
136
+ var _a;
137
+ this.setAttribute("open", "");
138
+ (_a = this.root.querySelector(".header")) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-expanded", "true");
139
+ // Dispatch event for accordion parent
140
+ this.dispatchEvent(new CustomEvent("accordion-toggle", { bubbles: true }));
141
+ // Animate open
142
+ if (this.contentEl) {
143
+ const content = this.root.querySelector(".content");
144
+ this.contentHeight = (content === null || content === void 0 ? void 0 : content.offsetHeight) || 0;
145
+ springAnimate(0, this.contentHeight, (v) => {
146
+ this.contentEl.style.height = `${v}px`;
147
+ }, gentleSpring);
148
+ }
149
+ }
150
+ close() {
151
+ var _a;
152
+ this.removeAttribute("open");
153
+ (_a = this.root.querySelector(".header")) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-expanded", "false");
154
+ // Animate close
155
+ if (this.contentEl) {
156
+ const currentHeight = this.contentEl.offsetHeight;
157
+ springAnimate(currentHeight, 0, (v) => {
158
+ this.contentEl.style.height = `${v}px`;
159
+ }, gentleSpring);
160
+ }
161
+ }
162
+ attributeChangedCallback(name, oldValue, newValue) {
163
+ if (name === "open" && this.contentEl) {
164
+ if (newValue !== null && oldValue === null) {
165
+ // Opening
166
+ }
167
+ else if (newValue === null && oldValue !== null) {
168
+ // Closing handled in close()
169
+ }
170
+ }
171
+ }
172
+ }
173
+ customElements.define("glass-accordion-item", GlassAccordionItem);
@@ -0,0 +1,9 @@
1
+ import { BaseElement } from "../core/base-element.js";
2
+ export declare class GlassAvatar extends BaseElement {
3
+ static get observedAttributes(): string[];
4
+ connectedCallback(): void;
5
+ attributeChangedCallback(): void;
6
+ }
7
+ export declare class GlassAvatarGroup extends BaseElement {
8
+ connectedCallback(): void;
9
+ }
@@ -0,0 +1,98 @@
1
+ import { BaseElement } from "../core/base-element.js";
2
+ export class GlassAvatar extends BaseElement {
3
+ static get observedAttributes() {
4
+ return ["src", "alt", "size", "status"];
5
+ }
6
+ connectedCallback() {
7
+ const src = this.getAttribute("src");
8
+ const alt = this.getAttribute("alt") || "";
9
+ const size = this.getAttribute("size") || "md";
10
+ const status = this.getAttribute("status");
11
+ this.mount(`
12
+ <div class="avatar ${size}">
13
+ ${src
14
+ ? `<img src="${src}" alt="${alt}" />`
15
+ : `<span class="initials"><slot></slot></span>`}
16
+ ${status ? `<span class="status ${status}"></span>` : ""}
17
+ </div>
18
+ `, `
19
+ :host {
20
+ display: inline-block;
21
+ }
22
+ .avatar {
23
+ position: relative;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ border-radius: 50%;
28
+ background: var(--lg-bg);
29
+ backdrop-filter: blur(var(--lg-blur));
30
+ border: 2px solid var(--lg-border);
31
+ overflow: hidden;
32
+ }
33
+
34
+ /* Sizes */
35
+ .avatar.xs { width: 24px; height: 24px; font-size: 0.625rem; }
36
+ .avatar.sm { width: 32px; height: 32px; font-size: 0.75rem; }
37
+ .avatar.md { width: 40px; height: 40px; font-size: 0.875rem; }
38
+ .avatar.lg { width: 56px; height: 56px; font-size: 1.125rem; }
39
+ .avatar.xl { width: 80px; height: 80px; font-size: 1.5rem; }
40
+
41
+ img {
42
+ width: 100%;
43
+ height: 100%;
44
+ object-fit: cover;
45
+ }
46
+ .initials {
47
+ font-weight: 600;
48
+ text-transform: uppercase;
49
+ color: rgba(255, 255, 255, 0.9);
50
+ }
51
+ .status {
52
+ position: absolute;
53
+ bottom: 0;
54
+ right: 0;
55
+ width: 25%;
56
+ height: 25%;
57
+ min-width: 8px;
58
+ min-height: 8px;
59
+ border-radius: 50%;
60
+ border: 2px solid rgba(18, 18, 22, 0.9);
61
+ }
62
+ .status.online { background: #30d158; }
63
+ .status.offline { background: #8e8e93; }
64
+ .status.busy { background: #ff453a; }
65
+ .status.away { background: #ffd60a; }
66
+ `);
67
+ }
68
+ attributeChangedCallback() {
69
+ // Re-render on attribute changes
70
+ if (this.isConnected) {
71
+ this.connectedCallback();
72
+ }
73
+ }
74
+ }
75
+ customElements.define("glass-avatar", GlassAvatar);
76
+ // Avatar group for stacking
77
+ export class GlassAvatarGroup extends BaseElement {
78
+ connectedCallback() {
79
+ this.mount(`<slot></slot>`, `
80
+ :host {
81
+ display: inline-flex;
82
+ flex-direction: row-reverse;
83
+ }
84
+ ::slotted(glass-avatar) {
85
+ margin-left: -0.75rem;
86
+ transition: transform 0.2s ease;
87
+ }
88
+ ::slotted(glass-avatar:hover) {
89
+ transform: translateY(-4px);
90
+ z-index: 1;
91
+ }
92
+ ::slotted(glass-avatar:last-child) {
93
+ margin-left: 0;
94
+ }
95
+ `);
96
+ }
97
+ }
98
+ customElements.define("glass-avatar-group", GlassAvatarGroup);
@@ -0,0 +1,10 @@
1
+ import { BaseElement } from "../core/base-element.js";
2
+ export declare class GlassBadge extends BaseElement {
3
+ static get observedAttributes(): string[];
4
+ connectedCallback(): void;
5
+ }
6
+ export declare class GlassBadgeWrapper extends BaseElement {
7
+ static get observedAttributes(): string[];
8
+ connectedCallback(): void;
9
+ attributeChangedCallback(): void;
10
+ }
@@ -0,0 +1,151 @@
1
+ import { BaseElement } from "../core/base-element.js";
2
+ export class GlassBadge extends BaseElement {
3
+ static get observedAttributes() {
4
+ return ["variant", "size", "dot"];
5
+ }
6
+ connectedCallback() {
7
+ const variant = this.getAttribute("variant") || "default";
8
+ const size = this.getAttribute("size") || "md";
9
+ const isDot = this.hasAttribute("dot");
10
+ this.mount(`
11
+ <span class="badge ${variant} ${size} ${isDot ? 'dot' : ''}">
12
+ ${isDot ? '' : '<slot></slot>'}
13
+ </span>
14
+ `, `
15
+ :host {
16
+ display: inline-block;
17
+ }
18
+ .badge {
19
+ display: inline-flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ padding: 0.25rem 0.6rem;
23
+ border-radius: 999px;
24
+ font-size: 0.75rem;
25
+ font-weight: 600;
26
+ background: var(--lg-bg);
27
+ backdrop-filter: blur(var(--lg-blur));
28
+ border: 1px solid var(--lg-border);
29
+ }
30
+
31
+ /* Sizes */
32
+ .badge.sm { padding: 0.125rem 0.4rem; font-size: 0.625rem; }
33
+ .badge.md { padding: 0.25rem 0.6rem; font-size: 0.75rem; }
34
+ .badge.lg { padding: 0.375rem 0.75rem; font-size: 0.875rem; }
35
+
36
+ /* Dot mode */
37
+ .badge.dot {
38
+ width: 8px;
39
+ height: 8px;
40
+ padding: 0;
41
+ min-width: 8px;
42
+ }
43
+ .badge.dot.sm { width: 6px; height: 6px; }
44
+ .badge.dot.lg { width: 10px; height: 10px; }
45
+
46
+ /* Variants */
47
+ .badge.default {
48
+ background: rgba(255, 255, 255, 0.15);
49
+ }
50
+ .badge.primary {
51
+ background: var(--lg-accent);
52
+ border-color: transparent;
53
+ color: white;
54
+ }
55
+ .badge.success {
56
+ background: rgba(48, 209, 88, 0.2);
57
+ border-color: rgba(48, 209, 88, 0.4);
58
+ color: #30d158;
59
+ }
60
+ .badge.warning {
61
+ background: rgba(255, 214, 10, 0.2);
62
+ border-color: rgba(255, 214, 10, 0.4);
63
+ color: #ffd60a;
64
+ }
65
+ .badge.error {
66
+ background: rgba(255, 69, 58, 0.2);
67
+ border-color: rgba(255, 69, 58, 0.4);
68
+ color: #ff453a;
69
+ }
70
+ .badge.info {
71
+ background: rgba(90, 200, 250, 0.2);
72
+ border-color: rgba(90, 200, 250, 0.4);
73
+ color: #5ac8fa;
74
+ }
75
+
76
+ /* Dot variants */
77
+ .badge.dot.success { background: #30d158; }
78
+ .badge.dot.warning { background: #ffd60a; }
79
+ .badge.dot.error { background: #ff453a; }
80
+ .badge.dot.info { background: #5ac8fa; }
81
+ .badge.dot.primary { background: #007aff; }
82
+ `);
83
+ }
84
+ }
85
+ customElements.define("glass-badge", GlassBadge);
86
+ // Badge wrapper for adding badge to other elements
87
+ export class GlassBadgeWrapper extends BaseElement {
88
+ static get observedAttributes() {
89
+ return ["count", "variant", "max", "dot"];
90
+ }
91
+ connectedCallback() {
92
+ const count = parseInt(this.getAttribute("count") || "0");
93
+ const max = parseInt(this.getAttribute("max") || "99");
94
+ const variant = this.getAttribute("variant") || "error";
95
+ const isDot = this.hasAttribute("dot");
96
+ const displayCount = count > max ? `${max}+` : String(count);
97
+ this.mount(`
98
+ <div class="wrapper">
99
+ <slot></slot>
100
+ ${(count > 0 || isDot) ? `
101
+ <span class="badge ${variant} ${isDot ? 'dot' : ''}">
102
+ ${isDot ? '' : displayCount}
103
+ </span>
104
+ ` : ''}
105
+ </div>
106
+ `, `
107
+ :host {
108
+ display: inline-block;
109
+ }
110
+ .wrapper {
111
+ position: relative;
112
+ display: inline-block;
113
+ }
114
+ .badge {
115
+ position: absolute;
116
+ top: -4px;
117
+ right: -4px;
118
+ min-width: 18px;
119
+ height: 18px;
120
+ padding: 0 5px;
121
+ border-radius: 999px;
122
+ font-size: 0.625rem;
123
+ font-weight: 700;
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ border: 2px solid rgba(18, 18, 22, 0.9);
128
+ z-index: 1;
129
+ }
130
+ .badge.dot {
131
+ width: 10px;
132
+ height: 10px;
133
+ min-width: 10px;
134
+ padding: 0;
135
+ top: -2px;
136
+ right: -2px;
137
+ }
138
+ .badge.error { background: #ff453a; color: white; }
139
+ .badge.success { background: #30d158; color: white; }
140
+ .badge.warning { background: #ffd60a; color: black; }
141
+ .badge.info { background: #5ac8fa; color: white; }
142
+ .badge.primary { background: #007aff; color: white; }
143
+ `);
144
+ }
145
+ attributeChangedCallback() {
146
+ if (this.isConnected) {
147
+ this.connectedCallback();
148
+ }
149
+ }
150
+ }
151
+ customElements.define("glass-badge-wrapper", GlassBadgeWrapper);
@@ -0,0 +1,6 @@
1
+ import { BaseElement } from "../core/base-element.js";
2
+ export declare class GlassButton extends BaseElement {
3
+ static get observedAttributes(): string[];
4
+ connectedCallback(): void;
5
+ attributeChangedCallback(name: string, _old: string, value: string): void;
6
+ }