Compare commits

..

2 Commits

Author SHA1 Message Date
Cory LaViska
4c81817bb5 allow valid chars in ids; fixes #1648 2025-10-24 08:18:09 -04:00
Cory LaViska
d2342a84e2 use getElementByID 2025-10-24 08:09:31 -04:00
8 changed files with 64 additions and 55 deletions

View File

@@ -12,22 +12,22 @@ export const themes = [
isPro: false, isPro: false,
fonts: { fonts: {
body: { body: {
name: 'OS Default (sans-serif)', name: 'OS Default',
css: 'ui-sans-serif, system-ui, sans-serif', css: 'ui-sans-serif, system-ui, sans-serif',
href: null, href: null,
}, },
heading: { heading: {
name: 'OS Default (sans-serif)', name: 'OS Default',
css: 'ui-sans-serif, system-ui, sans-serif', css: 'ui-sans-serif, system-ui, sans-serif',
href: null, href: null,
}, },
code: { code: {
name: 'OS Default (monospace)', name: 'OS Default',
css: 'ui-monospace, monospace', css: 'ui-monospace, monospace',
href: null, href: null,
}, },
longform: { longform: {
name: 'OS Default (serif)', name: 'OS Default',
css: 'ui-serif, serif', css: 'ui-serif, serif',
href: null, href: null,
}, },
@@ -82,7 +82,7 @@ export const themes = [
href: 'https://fonts.bunny.net/css2?family=Quicksand:wght@300..700&display=swap', href: 'https://fonts.bunny.net/css2?family=Quicksand:wght@300..700&display=swap',
}, },
code: { code: {
name: 'OS Default (monospace)', name: 'OS Default',
css: 'ui-monospace, monospace', css: 'ui-monospace, monospace',
href: null, href: null,
}, },
@@ -132,22 +132,22 @@ export const themes = [
isPro: false, isPro: false,
fonts: { fonts: {
body: { body: {
name: 'OS Default (sans-serif)', name: 'OS Default',
css: 'ui-sans-serif, system-ui, sans-serif', css: 'ui-sans-serif, system-ui, sans-serif',
href: null, href: null,
}, },
heading: { heading: {
name: 'OS Default (sans-serif)', name: 'OS Default',
css: 'ui-sans-serif, system-ui, sans-serif', css: 'ui-sans-serif, system-ui, sans-serif',
href: null, href: null,
}, },
code: { code: {
name: 'OS Default (monospace)', name: 'OS Default',
css: 'ui-monospace, monospace', css: 'ui-monospace, monospace',
href: null, href: null,
}, },
longform: { longform: {
name: 'OS Default (serif)', name: 'OS Default',
css: 'ui-serif, serif', css: 'ui-serif, serif',
href: null, href: null,
}, },
@@ -442,7 +442,7 @@ export const themes = [
href: 'https://fonts.bunny.net/css2?family=Lora:ital,wght@0,400..700;1,400..700&display=swap', href: 'https://fonts.bunny.net/css2?family=Lora:ital,wght@0,400..700;1,400..700&display=swap',
}, },
code: { code: {
name: 'OS Default (monospace)', name: 'OS Default',
css: 'ui-monospace, monospace', css: 'ui-monospace, monospace',
href: null, href: null,
}, },
@@ -562,7 +562,7 @@ export const themes = [
href: 'https://fonts.bunny.net/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap', href: 'https://fonts.bunny.net/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap',
}, },
code: { code: {
name: 'OS Default (monospace)', name: 'OS Default',
css: 'ui-monospace, monospace', css: 'ui-monospace, monospace',
href: null, href: null,
}, },
@@ -622,12 +622,12 @@ export const themes = [
href: 'https://fonts.bunny.net/css2?family=Inter:ital,wght@0,100..900;1,100..900&display=swap', href: 'https://fonts.bunny.net/css2?family=Inter:ital,wght@0,100..900;1,100..900&display=swap',
}, },
code: { code: {
name: 'OS Default (monospace)', name: 'OS Default',
css: 'ui-monospace, monospace', css: 'ui-monospace, monospace',
href: null, href: null,
}, },
longform: { longform: {
name: 'OS Default (serif)', name: 'OS Default',
css: 'ui-serif, serif', css: 'ui-serif, serif',
href: null, href: null,
}, },

View File

@@ -2,25 +2,22 @@
pre[id*='code-block-'] { pre[id*='code-block-'] {
color-scheme: dark; color-scheme: dark;
color: white; color: white;
background-color: var(--code-background, var(--wa-color-neutral-20)); background-color: var(--wa-color-neutral-20);
/* Ensures a discernible background color in dark mode /* Ensures a discernible background color in dark mode
* Useful for themes that use gray-20 as --wa-color-surface-default */ * Useful for themes that use gray-20 as --wa-color-surface-default */
.wa-dark & { .wa-dark & {
background-color: var(--code-background-dark, var(--wa-color-surface-lowered)); background-color: var(--wa-color-surface-lowered);
} }
} }
.code-comment, .code-comment,
.code-prolog, .code-prolog,
.code-doctype, .code-doctype,
.code-cdata { .code-cdata,
color: var(--code-comment, var(--wa-color-gray-70));
}
.code-operator, .code-operator,
.code-punctuation { .code-punctuation {
color: var(--code-operator, var(--wa-color-gray-70)); color: var(--wa-color-gray-70);
} }
.code-namespace { .code-namespace {
@@ -31,27 +28,24 @@ pre[id*='code-block-'] {
.code-keyword, .code-keyword,
.code-tag, .code-tag,
.code-url { .code-url {
color: var(--code-keyword, var(--wa-color-indigo-70)); color: var(--wa-color-indigo-70);
} }
.code-symbol, .code-symbol,
.code-deleted, .code-deleted,
.code-important { .code-important {
color: var(--code-error, var(--wa-color-red-70)); color: var(--wa-color-red-70);
}
.code-string,
.code-char,
.code-constant {
color: var(--code-string, var(--wa-color-green-70));
} }
.code-boolean, .code-boolean,
.code-constant,
.code-selector, .code-selector,
.code-attr-name, .code-attr-name,
.code-string,
.code-char,
.code-builtin, .code-builtin,
.code-inserted { .code-inserted {
color: var(--code-literal, var(--wa-color-green-70)); color: var(--wa-color-green-70);
} }
.code-atrule, .code-atrule,
@@ -61,7 +55,7 @@ pre[id*='code-block-'] {
.code-function, .code-function,
.code-class-name, .code-class-name,
.code-regex { .code-regex {
color: var(--code-value, var(--wa-color-blue-70)); color: var(--wa-color-blue-70);
} }
.code-important, .code-important,

View File

@@ -24,8 +24,6 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
- Added the Kazakh translation [pr:1496] - Added the Kazakh translation [pr:1496]
- Added docs for code completion for VS Code and JetBrains [pr:1550] - Added docs for code completion for VS Code and JetBrains [pr:1550]
- Added back the missing `form-control-label` part to `<wa-textarea>` for consistency with other form controls [pr:1533] - Added back the missing `form-control-label` part to `<wa-textarea>` for consistency with other form controls [pr:1533]
- Added focus delegation to `<wa-button>` to ensure tabbing works properly when using `tabindex` [issue:1622]
- Added [text utilities](/docs/utilities/text/) for longform text, form control text, font sizes, font weights, text color, and truncation [pr:1602]
- Fixed a bug in `<wa-button>` where slotted badges weren't properly positioned in buttons with an `href` [issue:1377] - Fixed a bug in `<wa-button>` where slotted badges weren't properly positioned in buttons with an `href` [issue:1377]
- Fixed focus outline styles in `<wa-details>` and native `<details>` [issue:1456] - Fixed focus outline styles in `<wa-details>` and native `<details>` [issue:1456]
- Fixed focus outline styles in `<wa-scroller>`, `<wa-dialog>`, and `<wa-drawer>` [issue:1484] - Fixed focus outline styles in `<wa-scroller>`, `<wa-dialog>`, and `<wa-drawer>` [issue:1484]
@@ -38,10 +36,11 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
- Fixed a bug in `<wa-tooltip>` that prevented tooltips from showing when disconnecting and then reconnecting to the DOM [issue:1595] - Fixed a bug in `<wa-tooltip>` that prevented tooltips from showing when disconnecting and then reconnecting to the DOM [issue:1595]
- Fixed a bug that caused the required `*` in form labels to have incorrect spacing in `<wa-checkbox>` and `<wa-switch>` [issue:1472] - Fixed a bug that caused the required `*` in form labels to have incorrect spacing in `<wa-checkbox>` and `<wa-switch>` [issue:1472]
- Fixed a bug in `<wa-dialog>` and `<wa-drawer>` that caused the component to prematurely hide when certain child elements are used [pr:1636] - Fixed a bug in `<wa-dialog>` and `<wa-drawer>` that caused the component to prematurely hide when certain child elements are used [pr:1636]
- Fixed a bug in `<wa-popover>` and `<wa-tooltip>` that prevented dots and other valid ID characters from being used [issue:1648]
- Improved autofill styles in `<wa-input>` so they span the entire width of the visual input [issue:1439] - Improved autofill styles in `<wa-input>` so they span the entire width of the visual input [issue:1439]
- Improved [text utilities](/docs/utilities/text/) so that each size modifier always exactly matches the applied font size [pr:1602]
- Improved Native Styles to use the `--wa-font-weight-code` design token
- Modified `<wa-slider>` to only show the tooltip on the handle being dragged when in range mode [issue:1320] - Modified `<wa-slider>` to only show the tooltip on the handle being dragged when in range mode [issue:1320]
- Improved [text utilities](/docs/utilities/text/) so that each size modifier always exactly matches the applied font size [pr:1602]
- Added [text utilities](/docs/utilities/text/) for longform text, form control text, font sizes, font weights, text color, and truncation [pr:1602]
- Upgraded `<wa-page>` from _experimental_ to _stable_ - Upgraded `<wa-page>` from _experimental_ to _stable_
## 3.0.0-beta.6 ## 3.0.0-beta.6

View File

@@ -41,7 +41,6 @@ import styles from './button.css';
*/ */
@customElement('wa-button') @customElement('wa-button')
export default class WaButton extends WebAwesomeFormAssociatedElement { export default class WaButton extends WebAwesomeFormAssociatedElement {
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
static css = [styles, variantStyles, sizeStyles]; static css = [styles, variantStyles, sizeStyles];
static get validators() { static get validators() {

View File

@@ -230,7 +230,7 @@ export default class WaPopover extends WebAwesomeElement {
return; return;
} }
const newAnchor = this.for ? rootNode.querySelector(`#${this.for}`) : null; const newAnchor = this.for ? rootNode.getElementById(this.for) : null;
const oldAnchor = this.anchor; const oldAnchor = this.anchor;
if (newAnchor === oldAnchor) { if (newAnchor === oldAnchor) {

View File

@@ -690,8 +690,8 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
if (!this.withTooltip) return; if (!this.withTooltip) return;
// Show only the active tooltip, hide the other // Show only the active tooltip, hide the other
const tooltipMin = this.shadowRoot?.querySelector('#tooltip-thumb-min') as WaTooltip; const tooltipMin = this.shadowRoot?.getElementById('tooltip-thumb-min') as WaTooltip;
const tooltipMax = this.shadowRoot?.querySelector('#tooltip-thumb-max') as WaTooltip; const tooltipMax = this.shadowRoot?.getElementById('tooltip-thumb-max') as WaTooltip;
if (this.activeThumb === 'min') { if (this.activeThumb === 'min') {
if (tooltipMin) tooltipMin.open = true; if (tooltipMin) tooltipMin.open = true;
@@ -705,8 +705,8 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
private hideRangeTooltips() { private hideRangeTooltips() {
if (!this.withTooltip) return; if (!this.withTooltip) return;
const tooltipMin = this.shadowRoot?.querySelector('#tooltip-thumb-min') as WaTooltip; const tooltipMin = this.shadowRoot?.getElementById('tooltip-thumb-min') as WaTooltip;
const tooltipMax = this.shadowRoot?.querySelector('#tooltip-thumb-max') as WaTooltip; const tooltipMax = this.shadowRoot?.getElementById('tooltip-thumb-max') as WaTooltip;
if (tooltipMin) tooltipMin.open = false; if (tooltipMin) tooltipMin.open = false;
if (tooltipMax) tooltipMax.open = false; if (tooltipMax) tooltipMax.open = false;

View File

@@ -137,8 +137,7 @@ export default class WaTooltip extends WebAwesomeElement {
this.eventController.abort(); this.eventController.abort();
if (this.anchor) { if (this.anchor) {
const label = this.anchor.getAttribute('aria-labelledby') || ''; this.removeFromAriaLabelledBy(this.anchor, this.id);
this.anchor.setAttribute('aria-labelledby', label.replace(this.id, ''));
} }
} }
@@ -202,6 +201,34 @@ export default class WaTooltip extends WebAwesomeElement {
return triggers.includes(triggerType); return triggers.includes(triggerType);
} }
/** Adds the tooltip ID to the aria-labelledby attribute */
private addToAriaLabelledBy(element: Element, id: string) {
const currentLabel = element.getAttribute('aria-labelledby') || '';
const labels = currentLabel.split(/\s+/).filter(Boolean);
// Only add if not already present
if (!labels.includes(id)) {
labels.push(id);
element.setAttribute('aria-labelledby', labels.join(' '));
}
}
/** Removes the tooltip ID from the aria-labelledby attribute */
private removeFromAriaLabelledBy(element: Element, id: string) {
const currentLabel = element.getAttribute('aria-labelledby') || '';
const labels = currentLabel.split(/\s+/).filter(Boolean);
// Remove the ID
const filteredLabels = labels.filter(label => label !== id);
if (filteredLabels.length > 0) {
element.setAttribute('aria-labelledby', filteredLabels.join(' '));
} else {
// Remove the attribute if empty
element.removeAttribute('aria-labelledby');
}
}
@watch('open', { waitUntilFirstUpdate: true }) @watch('open', { waitUntilFirstUpdate: true })
async handleOpenChange() { async handleOpenChange() {
if (this.open) { if (this.open) {
@@ -252,7 +279,7 @@ export default class WaTooltip extends WebAwesomeElement {
return; return;
} }
const newAnchor = this.for ? rootNode.querySelector(`#${this.for}`) : null; const newAnchor = this.for ? rootNode.getElementById(this.for) : null;
const oldAnchor = this.anchor; const oldAnchor = this.anchor;
if (newAnchor === oldAnchor) { if (newAnchor === oldAnchor) {
@@ -261,9 +288,6 @@ export default class WaTooltip extends WebAwesomeElement {
const { signal } = this.eventController; const { signal } = this.eventController;
// "\\b" is a space boundary, used for making sure we don't add the tooltip to aria-labelledby twice.
const labelRegex = new RegExp(`\\b${this.id}\\b`);
if (newAnchor) { if (newAnchor) {
/** /**
* We use `aria-labelledby` because it seems to have the most consistent screen reader experience. * We use `aria-labelledby` because it seems to have the most consistent screen reader experience.
@@ -272,10 +296,7 @@ export default class WaTooltip extends WebAwesomeElement {
* whereas with `aria-labelledby` it'll still read on first focus. The APG does and WAI-ARIA does recommend aria-describedby * whereas with `aria-labelledby` it'll still read on first focus. The APG does and WAI-ARIA does recommend aria-describedby
* so perhaps once we have cross-root aria, we can revisit this decision. * so perhaps once we have cross-root aria, we can revisit this decision.
*/ */
const currentLabel = newAnchor.getAttribute('aria-labelledby') || ''; this.addToAriaLabelledBy(newAnchor, this.id);
if (!currentLabel.match(labelRegex)) {
newAnchor.setAttribute('aria-labelledby', currentLabel + ' ' + this.id);
}
newAnchor.addEventListener('blur', this.handleBlur, { capture: true, signal }); newAnchor.addEventListener('blur', this.handleBlur, { capture: true, signal });
newAnchor.addEventListener('focus', this.handleFocus, { capture: true, signal }); newAnchor.addEventListener('focus', this.handleFocus, { capture: true, signal });
@@ -285,8 +306,7 @@ export default class WaTooltip extends WebAwesomeElement {
} }
if (oldAnchor) { if (oldAnchor) {
const label = oldAnchor.getAttribute('aria-labelledby') || ''; this.removeFromAriaLabelledBy(oldAnchor, this.id);
oldAnchor.setAttribute('aria-labelledby', label.replace(labelRegex, ''));
oldAnchor.removeEventListener('blur', this.handleBlur, { capture: true }); oldAnchor.removeEventListener('blur', this.handleBlur, { capture: true });
oldAnchor.removeEventListener('focus', this.handleFocus, { capture: true }); oldAnchor.removeEventListener('focus', this.handleFocus, { capture: true });
oldAnchor.removeEventListener('click', this.handleClick); oldAnchor.removeEventListener('click', this.handleClick);

View File

@@ -228,7 +228,6 @@
font-family: var(--wa-font-family-code); font-family: var(--wa-font-family-code);
font-size: var(--wa-font-size-smaller); font-size: var(--wa-font-size-smaller);
font-weight: var(--wa-font-weight-code);
border: solid var(--wa-border-width-s) color-mix(in oklab, currentColor, transparent 50%); border: solid var(--wa-border-width-s) color-mix(in oklab, currentColor, transparent 50%);
border-radius: var(--wa-border-radius-s); border-radius: var(--wa-border-radius-s);
@@ -282,7 +281,6 @@
font-family: var(--wa-font-family-code); font-family: var(--wa-font-family-code);
font-size: var(--wa-font-size-smaller); font-size: var(--wa-font-size-smaller);
font-weight: var(--wa-font-weight-code);
background-color: var(--wa-color-overlay-inline); background-color: var(--wa-color-overlay-inline);
border-radius: var(--wa-border-radius-s); border-radius: var(--wa-border-radius-s);
@@ -293,7 +291,6 @@
font-family: var(--wa-font-family-code); font-family: var(--wa-font-family-code);
font-size: var(--wa-font-size-smaller); font-size: var(--wa-font-size-smaller);
font-weight: var(--wa-font-weight-code);
white-space: pre; white-space: pre;
background-color: var(--wa-color-overlay-inline); background-color: var(--wa-color-overlay-inline);