@arclux/arc-ui 1.3.0 → 1.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arclux/arc-ui",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "ARC UI — Lit Web Components implementing the Arclight design system.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -149,6 +149,7 @@
149
149
  "./menu-item": "./src/shared/menu-item.js",
150
150
  "./menu-divider": "./src/shared/menu-divider.js",
151
151
  "./tokens": "./src/tokens.js",
152
+ "./tokens.css": "./src/tokens.css",
152
153
  "./icons/phosphor": "./src/icons/phosphor.js",
153
154
  "./icons/lucide": "./src/icons/lucide.js",
154
155
  "./icons/types": "./src/icons/types.d.ts"
@@ -0,0 +1,50 @@
1
+ import { css } from 'lit';
2
+
3
+ /**
4
+ * Shared button variant styles for primary / secondary / ghost.
5
+ *
6
+ * Each consuming component still defines:
7
+ * - Its own default variant (Button → primary, IconButton → ghost)
8
+ * - Its own :active transform scale
9
+ * - Sizes, layout, and component-specific rules
10
+ */
11
+ export const buttonVariantStyles = css`
12
+ /* ── Primary ── */
13
+ :host([variant="primary"]) .btn {
14
+ background: var(--accent-primary);
15
+ color: var(--bg-deep);
16
+ border-color: var(--accent-primary);
17
+ }
18
+ :host([variant="primary"]) .btn:hover {
19
+ box-shadow: var(--glow-primary);
20
+ }
21
+
22
+ /* ── Secondary ── */
23
+ :host([variant="secondary"]) .btn {
24
+ background: transparent;
25
+ color: var(--text-primary);
26
+ border-color: var(--border-default);
27
+ }
28
+ :host([variant="secondary"]) .btn:hover {
29
+ border-color: var(--accent-primary);
30
+ color: var(--accent-primary);
31
+ box-shadow: 0 0 20px var(--accent-primary-ring);
32
+ }
33
+
34
+ /* ── Ghost ── */
35
+ :host([variant="ghost"]) .btn {
36
+ background: transparent;
37
+ color: var(--text-muted);
38
+ border-color: transparent;
39
+ }
40
+ :host([variant="ghost"]) .btn:hover {
41
+ color: var(--text-primary);
42
+ background: var(--bg-hover);
43
+ }
44
+
45
+ /* ── Focus ── */
46
+ .btn:focus-visible { outline: none; box-shadow: var(--focus-glow); }
47
+
48
+ /* ── Disabled ── */
49
+ :host([disabled]) .btn { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
50
+ `;
@@ -0,0 +1,42 @@
1
+ import { css } from 'lit';
2
+
3
+ /**
4
+ * Shared card shell + hover styles for Card / FeatureCard.
5
+ *
6
+ * Provides the 1px-padding border trick, hover gradient border,
7
+ * inner box-shadow glow, and focus ring. Each consumer keeps its
8
+ * own inner layout, content, and responsive overrides.
9
+ */
10
+ export const cardHoverStyles = css`
11
+ .card {
12
+ position: relative;
13
+ border-radius: var(--radius-lg);
14
+ padding: 1px;
15
+ background: var(--border-subtle);
16
+ transition: background var(--transition-slow);
17
+ text-decoration: none;
18
+ display: flex;
19
+ flex-direction: column;
20
+ }
21
+
22
+ .card:hover {
23
+ background: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.3), rgba(var(--accent-secondary-rgb),0.15), var(--border-default));
24
+ }
25
+
26
+ .card__inner {
27
+ position: relative;
28
+ background: var(--bg-card);
29
+ border-radius: calc(var(--radius-lg) - 1px);
30
+ flex: 1;
31
+ min-height: 0;
32
+ display: flex;
33
+ flex-direction: column;
34
+ transition: box-shadow var(--transition-slow);
35
+ }
36
+
37
+ .card:hover .card__inner {
38
+ box-shadow: inset 0 1px 0 var(--bg-hover), var(--glow-card-hover);
39
+ }
40
+
41
+ .card:focus-visible { outline: none; box-shadow: var(--focus-glow); border-radius: var(--radius-lg); }
42
+ `;
@@ -4,6 +4,7 @@ import './accordion-item.js';
4
4
 
5
5
  export class ArcAccordion extends LitElement {
6
6
  static properties = {
7
+ multiple: { type: Boolean, reflect: true },
7
8
  _items: { state: true },
8
9
  _openItems: { state: true },
9
10
  };
@@ -98,6 +99,7 @@ export class ArcAccordion extends LitElement {
98
99
 
99
100
  constructor() {
100
101
  super();
102
+ this.multiple = false;
101
103
  this._items = [];
102
104
  this._openItems = new Set();
103
105
  }
@@ -112,6 +114,7 @@ export class ArcAccordion extends LitElement {
112
114
  if (next.has(index)) {
113
115
  next.delete(index);
114
116
  } else {
117
+ if (!this.multiple) next.clear();
115
118
  next.add(index);
116
119
  }
117
120
  this._openItems = next;
@@ -3,15 +3,17 @@ import { tokenStyles } from '../shared-styles.js';
3
3
 
4
4
  export class ArcAvatar extends LitElement {
5
5
  static properties = {
6
- src: { type: String },
7
- name: { type: String, reflect: true },
8
- size: { type: String, reflect: true },
6
+ src: { type: String },
7
+ name: { type: String, reflect: true },
8
+ size: { type: String, reflect: true },
9
+ shape: { type: String, reflect: true },
10
+ status: { type: String, reflect: true },
9
11
  };
10
12
 
11
13
  static styles = [
12
14
  tokenStyles,
13
15
  css`
14
- :host { display: inline-flex; }
16
+ :host { display: inline-flex; position: relative; }
15
17
 
16
18
  .avatar {
17
19
  display: inline-flex;
@@ -48,10 +50,36 @@ export class ArcAvatar extends LitElement {
48
50
  user-select: none;
49
51
  }
50
52
 
53
+ /* Shape variants */
54
+ :host([shape="square"]) .avatar,
55
+ :host([shape="square"]) .avatar__img { border-radius: var(--radius-md); }
56
+ :host([shape="rounded"]) .avatar,
57
+ :host([shape="rounded"]) .avatar__img { border-radius: var(--radius-lg); }
58
+
51
59
  :host([size="sm"]) .avatar__initials { font-size: var(--text-xs); }
52
60
  :host([size="md"]) .avatar__initials { font-size: var(--text-sm); }
53
61
  :host([size="lg"]) .avatar__initials { font-size: var(--text-lg); }
54
62
 
63
+ /* Status indicator */
64
+ .avatar__status {
65
+ position: absolute;
66
+ bottom: 0;
67
+ right: 0;
68
+ width: 10px;
69
+ height: 10px;
70
+ border-radius: var(--radius-full);
71
+ border: 2px solid var(--bg-deep);
72
+ box-sizing: border-box;
73
+ }
74
+
75
+ :host([size="sm"]) .avatar__status { width: 8px; height: 8px; }
76
+ :host([size="lg"]) .avatar__status { width: 14px; height: 14px; border-width: 3px; }
77
+
78
+ :host([status="online"]) .avatar__status { background: var(--color-success); }
79
+ :host([status="offline"]) .avatar__status { background: var(--text-ghost); }
80
+ :host([status="busy"]) .avatar__status { background: var(--color-error); }
81
+ :host([status="away"]) .avatar__status { background: var(--color-warning); }
82
+
55
83
  @media (prefers-reduced-motion: reduce) {
56
84
  :host *,
57
85
  :host *::before,
@@ -69,6 +97,8 @@ export class ArcAvatar extends LitElement {
69
97
  this.src = '';
70
98
  this.name = '';
71
99
  this.size = 'md';
100
+ this.shape = 'circle';
101
+ this.status = '';
72
102
  }
73
103
 
74
104
  /** @private */
@@ -85,6 +115,7 @@ export class ArcAvatar extends LitElement {
85
115
  <div class="avatar" part="avatar" role="img" aria-label=${this.name || 'Avatar'}>
86
116
  ${content}
87
117
  </div>
118
+ ${this.status ? html`<span class="avatar__status" part="status" aria-label=${this.status}></span>` : ''}
88
119
  `;
89
120
  }
90
121
  }
@@ -4,6 +4,7 @@ import { tokenStyles } from '../shared-styles.js';
4
4
  export class ArcBadge extends LitElement {
5
5
  static properties = {
6
6
  variant: { type: String, reflect: true },
7
+ size: { type: String, reflect: true },
7
8
  color: { type: String },
8
9
  };
9
10
 
@@ -74,6 +75,10 @@ export class ArcBadge extends LitElement {
74
75
  :host([variant="error"]:hover) .badge { box-shadow: 0 0 12px rgba(var(--color-error-rgb), 0.15); }
75
76
  :host([variant="info"]:hover) .badge { box-shadow: 0 0 12px rgba(var(--color-info-rgb), 0.15); }
76
77
 
78
+ /* Sizes */
79
+ :host([size="sm"]) .badge { font-size: calc(var(--text-xs) - 1px); padding: 2px var(--space-xs); }
80
+ :host([size="lg"]) .badge { font-size: var(--text-sm); padding: var(--space-sm) var(--space-md); }
81
+
77
82
  @media (prefers-reduced-motion: reduce) {
78
83
  :host *,
79
84
  :host *::before,
@@ -89,6 +94,7 @@ export class ArcBadge extends LitElement {
89
94
  constructor() {
90
95
  super();
91
96
  this.variant = 'default';
97
+ this.size = 'md';
92
98
  this.color = '';
93
99
  }
94
100
 
@@ -1,14 +1,18 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { statusVars } from '../status-styles.js';
3
4
  import './icon.js';
4
5
 
5
6
  export class ArcCallout extends LitElement {
6
7
  static properties = {
7
- variant: { type: String, reflect: true },
8
+ variant: { type: String, reflect: true },
9
+ dismissible: { type: Boolean, reflect: true },
10
+ _dismissed: { state: true },
8
11
  };
9
12
 
10
13
  static styles = [
11
14
  tokenStyles,
15
+ statusVars,
12
16
  css`
13
17
  :host { display: block; }
14
18
 
@@ -18,8 +22,8 @@ export class ArcCallout extends LitElement {
18
22
  gap: var(--space-sm);
19
23
  padding: var(--space-md) var(--space-lg);
20
24
  border-radius: var(--radius-md);
21
- border: 1px solid var(--border-default);
22
- background: var(--bg-card);
25
+ border: 1px solid rgba(var(--_status-rgb), 0.12);
26
+ background: rgba(var(--_status-rgb), 0.04);
23
27
  font-family: var(--font-body);
24
28
  font-size: var(--text-sm);
25
29
  line-height: 1.7;
@@ -36,6 +40,7 @@ export class ArcCallout extends LitElement {
36
40
  flex-shrink: 0;
37
41
  display: flex;
38
42
  align-items: center;
43
+ color: var(--_status-color);
39
44
  }
40
45
 
41
46
  .callout__label {
@@ -44,43 +49,42 @@ export class ArcCallout extends LitElement {
44
49
  font-weight: 600;
45
50
  letter-spacing: 1.5px;
46
51
  text-transform: uppercase;
52
+ color: var(--_status-color);
47
53
  }
48
54
 
49
55
  .callout__content {
50
56
  min-width: 0;
51
57
  }
52
58
 
53
- /* ── Info ── */
54
- :host([variant="info"]) .callout {
55
- border-color: rgba(var(--accent-primary-rgb), 0.12);
56
- background: rgba(var(--accent-primary-rgb), 0.04);
59
+ .callout__dismiss {
60
+ margin-left: auto;
61
+ background: none;
62
+ border: none;
63
+ color: var(--text-muted);
64
+ cursor: pointer;
65
+ padding: var(--space-xs);
66
+ border-radius: var(--radius-sm);
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ opacity: 0.6;
71
+ transition: opacity var(--transition-fast), background var(--transition-fast);
72
+ flex-shrink: 0;
57
73
  }
58
- :host([variant="info"]) .callout__icon { color: var(--accent-primary); }
59
- :host([variant="info"]) .callout__label { color: var(--accent-primary); }
60
74
 
61
- /* ── Warning ── */
62
- :host([variant="warning"]) .callout {
63
- border-color: rgba(var(--color-warning-rgb), 0.15);
64
- background: rgba(var(--color-warning-rgb), 0.04);
75
+ .callout__dismiss:hover {
76
+ opacity: 1;
77
+ background: rgba(var(--_status-rgb), 0.08);
65
78
  }
66
- :host([variant="warning"]) .callout__icon { color: var(--color-warning); }
67
- :host([variant="warning"]) .callout__label { color: var(--color-warning); }
68
79
 
69
- /* ── Tip ── */
70
- :host([variant="tip"]) .callout {
71
- border-color: rgba(var(--color-success-rgb), 0.15);
72
- background: rgba(var(--color-success-rgb), 0.04);
80
+ .callout__dismiss:focus-visible {
81
+ outline: none;
82
+ box-shadow: var(--focus-glow);
73
83
  }
74
- :host([variant="tip"]) .callout__icon { color: var(--color-success); }
75
- :host([variant="tip"]) .callout__label { color: var(--color-success); }
76
84
 
77
- /* ── Danger ── */
78
- :host([variant="danger"]) .callout {
79
- border-color: rgba(var(--color-error-rgb), 0.15);
80
- background: rgba(var(--color-error-rgb), 0.04);
85
+ :host(.dismissed) {
86
+ display: none;
81
87
  }
82
- :host([variant="danger"]) .callout__icon { color: var(--color-error); }
83
- :host([variant="danger"]) .callout__label { color: var(--color-error); }
84
88
 
85
89
  @media (prefers-reduced-motion: reduce) {
86
90
  :host *,
@@ -97,6 +101,8 @@ export class ArcCallout extends LitElement {
97
101
  constructor() {
98
102
  super();
99
103
  this.variant = 'info';
104
+ this.dismissible = false;
105
+ this._dismissed = false;
100
106
  }
101
107
 
102
108
  /** @private */
@@ -121,7 +127,15 @@ export class ArcCallout extends LitElement {
121
127
  return labels[this.variant] || labels.info;
122
128
  }
123
129
 
130
+ _dismiss() {
131
+ this._dismissed = true;
132
+ this.classList.add('dismissed');
133
+ this.dispatchEvent(new CustomEvent('arc-dismiss', { bubbles: true, composed: true }));
134
+ }
135
+
124
136
  render() {
137
+ if (this._dismissed) return html``;
138
+
125
139
  return html`
126
140
  <div class="callout" part="callout" role="note">
127
141
  <div class="callout__header" part="header">
@@ -129,6 +143,13 @@ export class ArcCallout extends LitElement {
129
143
  <slot name="icon"><arc-icon name=${this._getDefaultIcon()} size="sm"></arc-icon></slot>
130
144
  </span>
131
145
  <span class="callout__label" part="label">${this._getLabel()}</span>
146
+ ${this.dismissible ? html`
147
+ <button class="callout__dismiss" @click=${this._dismiss} aria-label="Dismiss" part="dismiss">
148
+ <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
149
+ <path d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"/>
150
+ </svg>
151
+ </button>
152
+ ` : ''}
132
153
  </div>
133
154
  <div class="callout__content" part="content">
134
155
  <slot></slot>
@@ -1,50 +1,41 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { cardHoverStyles } from '../card-styles.js';
3
4
 
4
5
  export class ArcCard extends LitElement {
5
6
  static properties = {
6
- href: { type: String },
7
- _hasFooter: { state: true },
7
+ href: { type: String },
8
+ padding: { type: String, reflect: true },
9
+ interactive: { type: Boolean, reflect: true },
10
+ _hasFooter: { state: true },
8
11
  };
9
12
 
10
13
  static styles = [
11
14
  tokenStyles,
15
+ cardHoverStyles,
12
16
  css`
13
17
  :host { display: block; }
14
18
 
15
- .card {
16
- position: relative;
17
- border-radius: var(--radius-lg);
18
- padding: 1px;
19
+ .card { height: 100%; }
20
+
21
+ /* Suppress hover effect when no href and not interactive */
22
+ :host(:not([href]):not([interactive])) .card:hover {
19
23
  background: var(--border-subtle);
20
- transition: background var(--transition-slow);
21
- text-decoration: none;
22
- display: flex;
23
- flex-direction: column;
24
- height: 100%;
25
24
  }
26
-
27
- :host([href]) .card:hover {
28
- background: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.3), rgba(var(--accent-secondary-rgb),0.15), var(--border-default));
25
+ :host(:not([href]):not([interactive])) .card:hover .card__inner {
26
+ box-shadow: none;
29
27
  }
30
28
 
29
+ :host([interactive]) .card { cursor: pointer; }
30
+
31
31
  .card__inner {
32
- position: relative;
33
- background: var(--bg-card);
34
- border-radius: calc(var(--radius-lg) - 1px);
35
32
  padding: var(--space-xl) var(--space-lg);
36
- flex: 1;
37
- min-height: 0;
38
- display: flex;
39
- flex-direction: column;
40
- transition: box-shadow var(--transition-slow);
41
- }
42
-
43
- .card:hover .card__inner {
44
- box-shadow: inset 0 1px 0 var(--bg-hover), var(--glow-card-hover);
45
33
  }
46
34
 
47
- .card:focus-visible { outline: none; box-shadow: var(--focus-glow); border-radius: var(--radius-lg); }
35
+ /* Padding variants */
36
+ :host([padding="none"]) .card__inner { padding: 0; }
37
+ :host([padding="sm"]) .card__inner { padding: var(--space-md) var(--space-sm); }
38
+ :host([padding="lg"]) .card__inner { padding: var(--space-2xl) var(--space-xl); }
48
39
 
49
40
  .card__body {
50
41
  flex: 1;
@@ -77,6 +68,8 @@ export class ArcCard extends LitElement {
77
68
  constructor() {
78
69
  super();
79
70
  this.href = '';
71
+ this.padding = 'md';
72
+ this.interactive = false;
80
73
  this._hasFooter = false;
81
74
  }
82
75
 
@@ -3,9 +3,10 @@ import { tokenStyles } from '../shared-styles.js';
3
3
 
4
4
  export class ArcDivider extends LitElement {
5
5
  static properties = {
6
- variant: { type: String, reflect: true },
7
- align: { type: String, reflect: true },
6
+ variant: { type: String, reflect: true },
7
+ align: { type: String, reflect: true },
8
8
  vertical: { type: Boolean, reflect: true },
9
+ label: { type: String },
9
10
  };
10
11
 
11
12
  static styles = [
@@ -128,6 +129,33 @@ export class ArcDivider extends LitElement {
128
129
  background: linear-gradient(180deg, transparent, rgba(var(--text-primary-rgb),0.35), transparent);
129
130
  }
130
131
 
132
+ /* Labeled divider */
133
+ .divider--labeled {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: var(--space-md);
137
+ height: auto;
138
+ background: none !important;
139
+ box-shadow: none !important;
140
+ }
141
+
142
+ .divider__line {
143
+ flex: 1;
144
+ height: 1px;
145
+ background: var(--gradient-divider);
146
+ }
147
+
148
+ .divider__label {
149
+ font-family: var(--font-accent);
150
+ font-size: var(--text-xs);
151
+ font-weight: 600;
152
+ letter-spacing: 1.5px;
153
+ text-transform: uppercase;
154
+ color: var(--text-muted);
155
+ white-space: nowrap;
156
+ flex-shrink: 0;
157
+ }
158
+
131
159
  @keyframes divider-shimmer {
132
160
  0%, 100% { background-position: 200% 0; }
133
161
  50% { background-position: -100% 0; }
@@ -143,9 +171,19 @@ export class ArcDivider extends LitElement {
143
171
  super();
144
172
  this.variant = 'subtle';
145
173
  this.vertical = false;
174
+ this.label = '';
146
175
  }
147
176
 
148
177
  render() {
178
+ if (this.label && !this.vertical) {
179
+ return html`
180
+ <div class="divider divider--labeled" role="separator" part="divider">
181
+ <span class="divider__line" part="line"></span>
182
+ <span class="divider__label" part="label">${this.label}</span>
183
+ <span class="divider__line" part="line"></span>
184
+ </div>
185
+ `;
186
+ }
149
187
  return html`<div class="divider" role="separator" aria-orientation=${this.vertical ? 'vertical' : 'horizontal'} part="divider"></div>`;
150
188
  }
151
189
  }
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { cardHoverStyles } from '../card-styles.js';
3
4
 
4
5
  export class ArcFeatureCard extends LitElement {
5
6
  static properties = {
@@ -11,42 +12,18 @@ export class ArcFeatureCard extends LitElement {
11
12
 
12
13
  static styles = [
13
14
  tokenStyles,
15
+ cardHoverStyles,
14
16
  css`
15
17
  :host { display: block; height: 100%; }
16
18
 
17
- .card {
18
- position: relative;
19
- border-radius: var(--radius-lg);
20
- height: 100%;
21
- padding: 1px;
22
- background: var(--border-subtle);
23
- transition: background var(--transition-slow);
24
- text-decoration: none;
25
- display: flex;
26
- flex-direction: column;
27
- }
28
-
29
- .card:hover {
30
- background: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.3), rgba(var(--accent-secondary-rgb),0.15), var(--border-default));
31
- }
19
+ .card { height: 100%; }
32
20
 
33
21
  .card__inner {
34
- position: relative;
35
- background: var(--bg-card);
36
- border-radius: calc(var(--radius-lg) - 1px);
37
22
  padding: var(--space-xl) var(--space-lg);
38
- display: flex;
39
- flex-direction: column;
40
23
  gap: var(--space-md);
41
- flex: 1;
42
- transition: box-shadow var(--transition-slow);
43
24
  z-index: 1;
44
25
  }
45
26
 
46
- .card:hover .card__inner {
47
- box-shadow: inset 0 1px 0 var(--bg-hover), var(--glow-card-hover);
48
- }
49
-
50
27
  .card__icon {
51
28
  width: 44px;
52
29
  height: 44px;
@@ -96,8 +73,6 @@ export class ArcFeatureCard extends LitElement {
96
73
 
97
74
  .card:hover .card__rule { opacity: 0.5; width: 48px; }
98
75
 
99
- .card:focus-visible { outline: none; box-shadow: var(--focus-glow); border-radius: var(--radius-lg); }
100
-
101
76
  @media (max-width: 768px) {
102
77
  .card__inner { padding: var(--space-lg) var(--space-md); }
103
78
  }
@@ -6,6 +6,7 @@ export class ArcSkeleton extends LitElement {
6
6
  variant: { type: String, reflect: true },
7
7
  width: { type: String },
8
8
  height: { type: String },
9
+ count: { type: Number },
9
10
  };
10
11
 
11
12
  static styles = [
@@ -43,6 +44,12 @@ export class ArcSkeleton extends LitElement {
43
44
  border-radius: var(--radius-md);
44
45
  }
45
46
 
47
+ .skeleton-group {
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: var(--space-sm);
51
+ }
52
+
46
53
  @media (prefers-reduced-motion: reduce) {
47
54
  :host *,
48
55
  :host *::before,
@@ -60,24 +67,28 @@ export class ArcSkeleton extends LitElement {
60
67
  this.variant = 'text';
61
68
  this.width = '';
62
69
  this.height = '';
70
+ this.count = 1;
63
71
  }
64
72
 
65
- render() {
73
+ _renderOne() {
66
74
  const styles = [
67
75
  this.width ? `width:${this.width}` : '',
68
76
  this.height ? `height:${this.height}` : '',
69
77
  this.variant === 'circle' && !this.height && this.width ? `height:${this.width}` : '',
70
78
  ].filter(Boolean).join(';');
71
79
 
80
+ return html`<div class="skeleton" part="skeleton" style=${styles}></div>`;
81
+ }
82
+
83
+ render() {
84
+ const n = Math.max(1, this.count);
85
+ if (n === 1) {
86
+ return html`<div role="status" aria-label="Loading" aria-busy="true">${this._renderOne()}</div>`;
87
+ }
72
88
  return html`
73
- <div
74
- class="skeleton"
75
- part="skeleton"
76
- role="status"
77
- aria-label="Loading"
78
- aria-busy="true"
79
- style=${styles}
80
- ></div>
89
+ <div class="skeleton-group" role="status" aria-label="Loading" aria-busy="true">
90
+ ${Array.from({ length: n }, () => this._renderOne())}
91
+ </div>
81
92
  `;
82
93
  }
83
94
  }