standards-ui
Version:
A foundational design system built with native Web Components. Includes comprehensive TypeScript types, JSDoc documentation, and component examples.
214 lines (184 loc) • 6.9 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: ds-label.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: ds-label.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/**
* @file ds-label.js
* @summary A custom Web Component that wraps a native `<label>` element.
* @description
* The `ds-label` component provides a styled and functional label element
* for associating text with form controls. It supports the `for` attribute
* to create explicit associations with form elements.
*
* - The content inside `<ds-label>...</ds-label>` is rendered via the default slot as the label text.
* - Supports the `for` attribute to associate the label with a form control by ID (like native `<label>`).
* - Supports ARIA attributes: `aria-label`, `aria-describedby` for accessibility.
* - Uses `part="label"` for styling via Shadow DOM.
* - Fires a `click` event when the label is clicked (native label behavior).
* - Warns in the console if no accessible name is provided, or if `for` references a missing element.
*
* @element ds-label
*
* @slot - Renders the label text content.
*
* @attr {string} for - The ID of the form control this label is associated with.
* @attr {string} aria-label - Accessible label for the label element (overrides text content).
* @attr {string} aria-describedby - Reference to element(s) describing the label.
*
* @property {string} htmlFor - Gets or sets the ID of the associated form control.
*
* @fires click - Fired when the label is clicked.
*
* @note Uses `part="label"` for styling via Shadow DOM.
* @note Warns if no accessible name (text or `aria-label`) is provided, or if `for` references a missing element.
*
* @example
* <!-- Basic label -->
* <ds-label>Username</ds-label>
*
* @example
* <!-- Label with explicit association -->
* <ds-label for="username-input">Username</ds-label>
* <ds-text-input id="username-input"></ds-text-input>
*
* @example
* <!-- Label with form control -->
* <ds-label for="email-field">Email Address</ds-label>
* <ds-text-input type="email" id="email-field" required></ds-text-input>
*
* @example
* <!-- Label with checkbox -->
* <ds-label for="agree-terms">I agree to the terms and conditions</ds-label>
* <ds-checkbox id="agree-terms" name="agree" value="yes"></ds-checkbox>
*/
import BaseComponent from './base-component.js';
class DsLabel extends BaseComponent {
constructor() {
// ARIA config for ds-label
const ariaConfig = {
staticAriaAttributes: {},
dynamicAriaAttributes: [
'aria-label',
'aria-describedby'
],
requiredAriaAttributes: [],
referenceAttributes: ['aria-describedby'],
};
const template = document.createElement('template');
template.innerHTML = `
<style>
url('/src/styles/styles.css');
:host {
display: block;
}
.wrapper {
width: 100%;
}
</style>
<div class="wrapper">
<label part="label">
<slot></slot>
</label>
</div>
`;
super({
template: template.innerHTML,
targetSelector: 'label',
ariaConfig,
events: ['click'],
observedAttributes: ['for']
});
this.label = this.shadowRoot.querySelector('label');
}
static get observedAttributes() {
return ['for', 'aria-label', 'aria-describedby'];
}
attributeChangedCallback(name, oldValue, newValue) {
super.attributeChangedCallback(name, oldValue, newValue);
if (oldValue === newValue) return;
switch (name) {
case 'for':
this.label.setAttribute('for', newValue || '');
break;
}
}
get htmlFor() {
return this.label.htmlFor;
}
set htmlFor(val) {
this.label.htmlFor = val;
}
// ARIA property accessors
get ariaLabel() {
const value = this.label.getAttribute('aria-label');
return value === null ? null : value;
}
set ariaLabel(val) {
if (val === null || val === undefined) {
this.label.removeAttribute('aria-label');
} else {
this.label.setAttribute('aria-label', val);
}
}
get ariaDescribedBy() {
const value = this.label.getAttribute('aria-describedby');
return value === null ? null : value;
}
set ariaDescribedBy(val) {
if (val === null || val === undefined) {
this.label.removeAttribute('aria-describedby');
} else {
this.label.setAttribute('aria-describedby', val);
}
}
// Override validateARIA for label-specific checks
validateARIA() {
const errors = super.validateARIA ? super.validateARIA() : [];
// Check for accessible association
const forAttr = this.label.getAttribute('for');
if (forAttr && !document.getElementById(forAttr)) {
errors.push(`Label 'for' attribute references missing element: ${forAttr}`);
}
// Accessible name check
const labelText = this.textContent.trim();
const ariaLabel = this.label.getAttribute('aria-label');
if (!labelText && !ariaLabel) {
errors.push('Label has no accessible name (text or aria-label required)');
}
return errors;
}
}
// Register the custom element
if (!customElements.get('ds-label')) {
customElements.define('ds-label', DsLabel);
}
// Export for use in other modules
export default DsLabel;</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="BaseComponent.html">BaseComponent</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Wed Aug 20 2025 19:54:53 GMT-0700 (Pacific Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>