edu-webcomponents 1.30.1 → 1.31.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/CHANGELOG.md CHANGED
@@ -4,10 +4,17 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ #### [v1.31.0](https://github.com/eduardocruzpalacios/edu-webcomponents/compare/v1.30.1...v1.31.0)
8
+
9
+ - feat: add EduLabel component [`d8945dd`](https://github.com/eduardocruzpalacios/edu-webcomponents/commit/d8945dd56f839fe34010f1859d99b54e3367bbd1)
10
+
7
11
  #### [v1.30.1](https://github.com/eduardocruzpalacios/edu-webcomponents/compare/v1.30.0...v1.30.1)
8
12
 
9
- - docs: include accessibility features in README.md [`938258a`](https://github.com/eduardocruzpalacios/edu-webcomponents/commit/938258ad9c4977d4f41a5e30d837c840e894bee4)
10
- - fix: format code [`f244d97`](https://github.com/eduardocruzpalacios/edu-webcomponents/commit/f244d9758426d3d8e0e051f222ff6ad05f3077a9)
13
+ > 20 January 2026
14
+
15
+ - docs: include accessibility features in README.md [`0876421`](https://github.com/eduardocruzpalacios/edu-webcomponents/commit/08764219fa28a92d178d398efe0b2f2ca83375d3)
16
+ - chore: release v1.30.1 [`1c13ee8`](https://github.com/eduardocruzpalacios/edu-webcomponents/commit/1c13ee8b6ec7af8f380af0c38b88136897125b27)
17
+ - fix: format code [`7e6c4bb`](https://github.com/eduardocruzpalacios/edu-webcomponents/commit/7e6c4bbde4565d526962650f2cabf339aff36a3a)
11
18
 
12
19
  #### [v1.30.0](https://github.com/eduardocruzpalacios/edu-webcomponents/compare/v1.29.0...v1.30.0)
13
20
 
package/README.md CHANGED
@@ -48,6 +48,72 @@ Or import individual components:
48
48
 
49
49
  ## 📚 Components
50
50
 
51
+ ### 🏷️ Badge
52
+
53
+ A small status indicator or label component perfect for displaying counts, categories, or status information.
54
+
55
+ **Properties:**
56
+ - `text` (String) - Badge text content (default: `''`)
57
+ - `variant` (String) - Color variant: `'default'`, `'primary'`, `'success'`, `'warning'`, `'error'`, or `'info'` (default: `'default'`)
58
+ - `size` (String) - Size variant: `'small'`, `'medium'`, or `'large'` (default: `'medium'`)
59
+ - `aria-label` (String) - Accessibility label
60
+ - `aria-live` (String) - Announces dynamic changes to screen readers: `'polite'` or `'assertive'`
61
+ - `decorative` (Boolean) - Marks badge as decorative (hidden from screen readers)
62
+
63
+ **Accessibility:**
64
+ - Uses `role="status"` for screen reader announcements (non-decorative badges)
65
+ - Supports `aria-live` attribute for dynamic content announcements
66
+ - Automatically adds `aria-atomic="true"` when `aria-live` is set
67
+ - Decorative mode (`decorative` property) hides badge from screen readers with `aria-hidden="true"`
68
+ - Customizable ARIA labels for better context
69
+ - Semantic color choices with sufficient contrast ratios
70
+ - Full dark mode support with optimized color schemes
71
+
72
+ **Usage:**
73
+
74
+ ```html
75
+ <!-- Basic badge -->
76
+ <edu-badge text="New"></edu-badge>
77
+
78
+ <!-- Colored variants -->
79
+ <edu-badge text="Active" variant="success"></edu-badge>
80
+ <edu-badge text="Pending" variant="warning"></edu-badge>
81
+ <edu-badge text="Error" variant="error"></edu-badge>
82
+ <edu-badge text="Info" variant="info"></edu-badge>
83
+ <edu-badge text="Primary" variant="primary"></edu-badge>
84
+
85
+ <!-- Different sizes -->
86
+ <edu-badge text="Small" size="small" variant="primary"></edu-badge>
87
+ <edu-badge text="Medium" size="medium" variant="primary"></edu-badge>
88
+ <edu-badge text="Large" size="large" variant="primary"></edu-badge>
89
+
90
+ <!-- Notification count with live updates -->
91
+ <span>Messages
92
+ <edu-badge
93
+ text="5"
94
+ variant="primary"
95
+ size="small"
96
+ aria-live="polite"
97
+ aria-label="5 unread messages">
98
+ </edu-badge>
99
+ </span>
100
+
101
+ <!-- Decorative badge (category tag) -->
102
+ <edu-badge text="JavaScript" variant="info" decorative></edu-badge>
103
+ ```
104
+
105
+ **JavaScript:**
106
+
107
+ ```javascript
108
+ import { EduBadge } from 'edu-webcomponents';
109
+
110
+ const badge = document.querySelector('edu-badge');
111
+ badge.text = '10';
112
+ badge.variant = 'error';
113
+ ```
114
+
115
+ ---
116
+
51
117
  ### 🔘 Button
52
118
 
53
119
  A customizable button component with hover effects and disabled state.
package/demo/index.html CHANGED
@@ -114,6 +114,51 @@
114
114
  A collection of reusable web components built with Lit
115
115
  </p>
116
116
 
117
+ <!-- Badge Component -->
118
+ <section class="section">
119
+ <h2>Badge</h2>
120
+ <p>Small status indicators and labels for displaying counts, categories, or status.</p>
121
+
122
+ <div class="demo-item">
123
+ <div class="demo-label">All Variants</div>
124
+ <div class="button-group">
125
+ <edu-badge .text=${'Default'} .variant=${'default'}></edu-badge>
126
+ <edu-badge .text=${'Primary'} .variant=${'primary'}></edu-badge>
127
+ <edu-badge .text=${'Success'} .variant=${'success'}></edu-badge>
128
+ <edu-badge .text=${'Warning'} .variant=${'warning'}></edu-badge>
129
+ <edu-badge .text=${'Error'} .variant=${'error'}></edu-badge>
130
+ <edu-badge .text=${'Info'} .variant=${'info'}></edu-badge>
131
+ </div>
132
+ </div>
133
+
134
+ <div class="demo-item">
135
+ <div class="demo-label">All Sizes</div>
136
+ <div class="button-group">
137
+ <edu-badge .text=${'Small'} .variant=${'primary'} .size=${'small'}></edu-badge>
138
+ <edu-badge .text=${'Medium'} .variant=${'primary'} .size=${'medium'}></edu-badge>
139
+ <edu-badge .text=${'Large'} .variant=${'primary'} .size=${'large'}></edu-badge>
140
+ </div>
141
+ </div>
142
+
143
+ <div class="demo-item">
144
+ <div class="demo-label">Notification Counts</div>
145
+ <div class="button-group">
146
+ <span>Messages <edu-badge .text=${'5'} .variant=${'primary'} .size=${'small'}></edu-badge></span>
147
+ <span>Alerts <edu-badge .text=${'12'} .variant=${'error'} .size=${'small'}></edu-badge></span>
148
+ <span>Updates <edu-badge .text=${'3'} .variant=${'info'} .size=${'small'}></edu-badge></span>
149
+ </div>
150
+ </div>
151
+
152
+ <div class="demo-item">
153
+ <div class="demo-label">Status Badges</div>
154
+ <div class="button-group">
155
+ <edu-badge .text=${'Active'} .variant=${'success'}></edu-badge>
156
+ <edu-badge .text=${'Pending'} .variant=${'warning'}></edu-badge>
157
+ <edu-badge .text=${'Inactive'} .variant=${'error'}></edu-badge>
158
+ </div>
159
+ </div>
160
+ </section>
161
+
117
162
  <!-- Button Component -->
118
163
  <section class="section">
119
164
  <h2>Button</h2>
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export { EduBadge } from './src/edu-badge/index.js';
1
2
  export { EduButton } from './src/edu-button/index.js';
2
3
  export { EduDivider } from './src/edu-divider/index.js';
3
4
  export { EduLoadingSpinner } from './src/edu-loading-spinner/index.js';
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "license": "MIT",
13
13
  "author": "edu-webcomponents",
14
- "version": "1.30.1",
14
+ "version": "1.31.0",
15
15
  "repository": {
16
16
  "type": "git",
17
17
  "url": "https://github.com/eduardocruzpalacios/edu-webcomponents"
@@ -0,0 +1,171 @@
1
+ import { html } from 'lit';
2
+ import './index.js';
3
+
4
+ export default {
5
+ title: 'Edu web components/EduBadge',
6
+ tags: ['autodocs'],
7
+ };
8
+
9
+ const createStory = args => {
10
+ const story = ({ text, variant, size } = args) => html`
11
+ <edu-badge .text=${text} .variant=${variant} .size=${size}></edu-badge>
12
+ `;
13
+ story.parameters = {
14
+ controls: { expanded: true },
15
+ docs: { source: { type: 'code' } },
16
+ };
17
+ story.argTypes = {
18
+ text: {
19
+ control: 'text',
20
+ description: 'Badge text content',
21
+ name: 'text',
22
+ },
23
+ variant: {
24
+ control: 'select',
25
+ options: ['default', 'primary', 'success', 'warning', 'error', 'info'],
26
+ description:
27
+ 'Color variant: default / primary / success / warning / error / info',
28
+ name: 'variant',
29
+ },
30
+ size: {
31
+ control: 'select',
32
+ options: ['small', 'medium', 'large'],
33
+ description: 'Size variant: small / medium / large',
34
+ name: 'size',
35
+ },
36
+ };
37
+ story.args = args;
38
+ return story;
39
+ };
40
+
41
+ const defaultArgs = {
42
+ text: 'Badge',
43
+ variant: 'default',
44
+ size: 'medium',
45
+ };
46
+
47
+ export const EduBadgeDefault = createStory({ ...defaultArgs });
48
+ export const EduBadgePrimary = createStory({
49
+ ...defaultArgs,
50
+ variant: 'primary',
51
+ });
52
+ export const EduBadgeSuccess = createStory({
53
+ ...defaultArgs,
54
+ variant: 'success',
55
+ });
56
+ export const EduBadgeWarning = createStory({
57
+ ...defaultArgs,
58
+ variant: 'warning',
59
+ });
60
+ export const EduBadgeError = createStory({ ...defaultArgs, variant: 'error' });
61
+ export const EduBadgeInfo = createStory({ ...defaultArgs, variant: 'info' });
62
+
63
+ export const EduBadgeSmall = createStory({
64
+ ...defaultArgs,
65
+ variant: 'primary',
66
+ size: 'small',
67
+ });
68
+ export const EduBadgeLarge = createStory({
69
+ ...defaultArgs,
70
+ variant: 'primary',
71
+ size: 'large',
72
+ });
73
+
74
+ export const AllVariants = () => {
75
+ const story = () => html`
76
+ <div
77
+ style="display: flex; gap: 1rem; flex-wrap: wrap; align-items: center;"
78
+ >
79
+ <edu-badge text="Default" variant="default"></edu-badge>
80
+ <edu-badge text="Primary" variant="primary"></edu-badge>
81
+ <edu-badge text="Success" variant="success"></edu-badge>
82
+ <edu-badge text="Warning" variant="warning"></edu-badge>
83
+ <edu-badge text="Error" variant="error"></edu-badge>
84
+ <edu-badge text="Info" variant="info"></edu-badge>
85
+ </div>
86
+ `;
87
+ story.parameters = {
88
+ controls: { disable: true },
89
+ docs: { source: { type: 'code' } },
90
+ };
91
+ return story();
92
+ };
93
+
94
+ export const AllSizes = () => {
95
+ const story = () => html`
96
+ <div
97
+ style="display: flex; gap: 1rem; flex-wrap: wrap; align-items: center;"
98
+ >
99
+ <edu-badge text="Small" variant="primary" size="small"></edu-badge>
100
+ <edu-badge text="Medium" variant="primary" size="medium"></edu-badge>
101
+ <edu-badge text="Large" variant="primary" size="large"></edu-badge>
102
+ </div>
103
+ `;
104
+ story.parameters = {
105
+ controls: { disable: true },
106
+ docs: { source: { type: 'code' } },
107
+ };
108
+ return story();
109
+ };
110
+
111
+ export const UseCases = () => {
112
+ const story = () => html`
113
+ <div style="display: flex; flex-direction: column; gap: 2rem;">
114
+ <div>
115
+ <h3>Status Indicators</h3>
116
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
117
+ <edu-badge text="Active" variant="success"></edu-badge>
118
+ <edu-badge text="Pending" variant="warning"></edu-badge>
119
+ <edu-badge text="Inactive" variant="error"></edu-badge>
120
+ </div>
121
+ </div>
122
+
123
+ <div>
124
+ <h3>Notification Counts</h3>
125
+ <div
126
+ style="display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;"
127
+ >
128
+ <span
129
+ >Messages
130
+ <edu-badge text="5" variant="primary" size="small"></edu-badge
131
+ ></span>
132
+ <span
133
+ >Alerts
134
+ <edu-badge text="12" variant="error" size="small"></edu-badge
135
+ ></span>
136
+ <span
137
+ >Updates <edu-badge text="3" variant="info" size="small"></edu-badge
138
+ ></span>
139
+ </div>
140
+ </div>
141
+
142
+ <div>
143
+ <h3>Category Tags</h3>
144
+ <div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
145
+ <edu-badge text="JavaScript" variant="info" size="small"></edu-badge>
146
+ <edu-badge
147
+ text="Web Components"
148
+ variant="primary"
149
+ size="small"
150
+ ></edu-badge>
151
+ <edu-badge text="Lit" variant="success" size="small"></edu-badge>
152
+ <edu-badge text="CSS" variant="warning" size="small"></edu-badge>
153
+ </div>
154
+ </div>
155
+
156
+ <div>
157
+ <h3>Version Badge</h3>
158
+ <div style="display: flex; gap: 1rem;">
159
+ <edu-badge text="v1.0.0" variant="default"></edu-badge>
160
+ <edu-badge text="Beta" variant="warning"></edu-badge>
161
+ <edu-badge text="New" variant="success"></edu-badge>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ `;
166
+ story.parameters = {
167
+ controls: { disable: true },
168
+ docs: { source: { type: 'code' } },
169
+ };
170
+ return story();
171
+ };
@@ -0,0 +1 @@
1
+ export { EduBadge } from './src/EduBadge.js';
@@ -0,0 +1,164 @@
1
+ import { html, css, LitElement } from 'lit';
2
+ import { colorsConstants, typographyConstants } from '../../stylesConstants.js';
3
+
4
+ export class EduBadge extends LitElement {
5
+ static styles = [
6
+ colorsConstants,
7
+ typographyConstants,
8
+ css`
9
+ :host {
10
+ display: inline-block;
11
+ }
12
+
13
+ .badge {
14
+ display: inline-flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ font-family: inherit;
18
+ font-weight: 600;
19
+ border-radius: 12px;
20
+ white-space: nowrap;
21
+ line-height: 1;
22
+ transition: background-color 0.2s ease-in-out;
23
+ }
24
+
25
+ /* Size variants */
26
+ .badge--small {
27
+ padding: 0.25rem 0.5rem;
28
+ font-size: 0.75rem;
29
+ }
30
+
31
+ .badge--medium {
32
+ padding: 0.375rem 0.75rem;
33
+ font-size: 0.875rem;
34
+ }
35
+
36
+ .badge--large {
37
+ padding: 0.5rem 1rem;
38
+ font-size: 1rem;
39
+ }
40
+
41
+ /* Color variants - Light mode */
42
+ .badge--default {
43
+ background-color: var(--greyLight);
44
+ color: var(--blackLight);
45
+ }
46
+
47
+ .badge--primary {
48
+ background-color: var(--primary);
49
+ color: var(--primaryForeground);
50
+ }
51
+
52
+ .badge--success {
53
+ background-color: #4caf50;
54
+ color: #ffffff;
55
+ }
56
+
57
+ .badge--warning {
58
+ background-color: #ff9800;
59
+ color: #000000;
60
+ }
61
+
62
+ .badge--error {
63
+ background-color: #f44336;
64
+ color: #ffffff;
65
+ }
66
+
67
+ .badge--info {
68
+ background-color: #2196f3;
69
+ color: #ffffff;
70
+ }
71
+
72
+ /* Dark mode support */
73
+ @media (prefers-color-scheme: dark) {
74
+ .badge--default {
75
+ background-color: var(--greyLight);
76
+ color: var(--primaryForeground);
77
+ }
78
+
79
+ .badge--primary {
80
+ background-color: #4db8ff;
81
+ color: #000000;
82
+ }
83
+
84
+ .badge--success {
85
+ background-color: #66bb6a;
86
+ color: #000000;
87
+ }
88
+
89
+ .badge--warning {
90
+ background-color: #ffa726;
91
+ color: #000000;
92
+ }
93
+
94
+ .badge--error {
95
+ background-color: #ef5350;
96
+ color: #ffffff;
97
+ }
98
+
99
+ .badge--info {
100
+ background-color: #42a5f5;
101
+ color: #000000;
102
+ }
103
+ }
104
+ `,
105
+ ];
106
+
107
+ static properties = {
108
+ text: { type: String },
109
+ variant: { type: String },
110
+ size: { type: String },
111
+ ariaLabel: { type: String, attribute: 'aria-label' },
112
+ ariaLive: { type: String, attribute: 'aria-live' },
113
+ decorative: { type: Boolean },
114
+ };
115
+
116
+ constructor() {
117
+ super();
118
+ this.text = '';
119
+ this.variant = 'default';
120
+ this.size = 'medium';
121
+ this.ariaLabel = '';
122
+ this.ariaLive = '';
123
+ this.decorative = false;
124
+ }
125
+
126
+ render() {
127
+ const classes = `badge badge--${this.size} badge--${this.variant}`;
128
+
129
+ // Decorative badges should be hidden from screen readers
130
+ if (this.decorative) {
131
+ return html`
132
+ <span class=${classes} aria-hidden="true"> ${this.text} </span>
133
+ `;
134
+ }
135
+
136
+ // Accessibility attributes
137
+ const ariaAttrs = {
138
+ role: 'status',
139
+ 'aria-label': this.ariaLabel || this.text,
140
+ };
141
+
142
+ // Add aria-live for dynamic content (like notification counts)
143
+ if (this.ariaLive) {
144
+ ariaAttrs['aria-live'] = this.ariaLive;
145
+ ariaAttrs['aria-atomic'] = 'true';
146
+ } else {
147
+ ariaAttrs['aria-live'] = 'off';
148
+ }
149
+
150
+ return html`
151
+ <span
152
+ class=${classes}
153
+ role=${ariaAttrs.role}
154
+ aria-label=${ariaAttrs['aria-label']}
155
+ aria-live=${ariaAttrs['aria-live']}
156
+ ?aria-atomic=${!!ariaAttrs['aria-atomic']}
157
+ >
158
+ ${this.text}
159
+ </span>
160
+ `;
161
+ }
162
+ }
163
+
164
+ customElements.define('edu-badge', EduBadge);
@@ -0,0 +1,20 @@
1
+ import { html } from 'lit';
2
+ import { fixture, expect } from '@open-wc/testing';
3
+
4
+ import '../index.js';
5
+
6
+ describe('EduBadge', () => {
7
+ it('renders the text content', async () => {
8
+ const text = 'Test Badge';
9
+ const el = await fixture(html`<edu-badge text=${text}></edu-badge>`);
10
+
11
+ const badge = el.shadowRoot.querySelector('.badge');
12
+ expect(badge.textContent.trim()).to.equal(text);
13
+ });
14
+
15
+ it('passes the a11y audit', async () => {
16
+ const el = await fixture(html`<edu-badge text="Badge"></edu-badge>`);
17
+
18
+ await expect(el).shadowDom.to.be.accessible();
19
+ });
20
+ });