Fix accessibility and markup bugs across preview pages and docs (#3082)

Co-authored-by: codecalm <codecalm@gmail.com>
This commit is contained in:
Bartosz-Doandcodecalm authored and GitHub committed 2026-09-18 00:16:52 +02:00
1 parent c99a436cc2
commit 364ccf801b
29 files changed
+197 -184

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/preview": patch
"@tabler/docs": patch
---
Fixed keyboard access and accessible names across preview pages, `Alert`, `Avatar`, `Rating`, `Tag` and `Wysiwyg` components.
+9 -9
View File
@@ -89,7 +89,7 @@ Add `alert-minor` for a quieter alert. It drops the tinted background and keeps
<div class="alert alert-minor alert-info alert-dismissible" role="alert">
<div class="alert-heading">Did you know?</div>
<div class="text-secondary">Here is something that you might like to know.</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
</div>
<div class="alert alert-minor alert-success" role="alert">
<div class="alert-heading">Everything worked</div>
@@ -102,7 +102,7 @@ Add `alert-minor` for a quieter alert. It drops the tinted background and keeps
Add a close button to let the user dismiss the alert. It stays on the page until they do.
```html
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
```
<Example>
@@ -222,7 +222,7 @@ Buttons don't inherit the alert's color, so you should set the proper class if y
<a href="#" class="btn btn-success">Okay</a>
<a href="#" class="btn">Cancel</a>
</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
</div>
<div class="alert alert-info alert-dismissible" role="alert">
<h3 class="mb-1">Some Title</h3>
@@ -231,7 +231,7 @@ Buttons don't inherit the alert's color, so you should set the proper class if y
<a href="#" class="btn btn-info">Okay</a>
<a href="#" class="btn">Cancel</a>
</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
</div>
<div class="alert alert-warning alert-dismissible" role="alert">
<h3 class="mb-1">Some Title</h3>
@@ -240,7 +240,7 @@ Buttons don't inherit the alert's color, so you should set the proper class if y
<a href="#" class="btn btn-warning">Okay</a>
<a href="#" class="btn">Cancel</a>
</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
</div>
<div class="alert alert-danger alert-dismissible" role="alert">
<h3 class="mb-1">Some Title</h3>
@@ -249,7 +249,7 @@ Buttons don't inherit the alert's color, so you should set the proper class if y
<a href="#" class="btn btn-danger">Okay</a>
<a href="#" class="btn">Cancel</a>
</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
</div>
</Example>
@@ -271,7 +271,7 @@ You can also use other elements, like icons and dismissible buttons, with this t
</div>
<div>Wow! Everything worked!</div>
</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
</div>
<div class="alert alert-important alert-danger alert-dismissible" role="alert">
<div class="d-flex">
@@ -280,7 +280,7 @@ You can also use other elements, like icons and dismissible buttons, with this t
</div>
<div>Your account has been deleted and can't be restored.</div>
</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
</div>
</Example>
@@ -318,7 +318,7 @@ You're not limited to the 4 default alert colors. You can use any [base color](/
<div>Be the first to see it!</div>
</div>
</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
</div>
</Example>
+9 -9
View File
@@ -467,15 +467,15 @@ Add `.btn-animate-icon` to move the icon on hover. The `-rotate`, `-shake`, `-pu
<Example>
<div class="btn-list">
<a class="btn btn-animate-icon"> Save <Icon name="arrow-right" class="icon-end" /> </a>
<a class="btn btn-animate-icon btn-animate-icon-rotate"> <Icon name="plus" /> Add </a>
<a class="btn btn-animate-icon btn-animate-icon-shake"> <Icon name="bell" /> Notifications </a>
<a class="btn btn-animate-icon btn-animate-icon-rotate"> <Icon name="settings" /> Settings </a>
<a class="btn btn-animate-icon btn-animate-icon-pulse"> <Icon name="heart" /> Love </a>
<a class="btn btn-animate-icon btn-animate-icon-rotate"> <Icon name="x" /> Close </a>
<a class="btn btn-animate-icon btn-animate-icon-tada"> <Icon name="check" /> Confirm </a>
<a class="btn btn-animate-icon"> Next <Icon name="chevron-right" class="icon-end" /> </a>
<a class="btn btn-animate-icon btn-animate-icon-move-start"> <Icon name="chevron-left" /> Previous </a>
<button type="button" class="btn btn-animate-icon"> Save <Icon name="arrow-right" class="icon-end" /> </button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-rotate"> <Icon name="plus" /> Add </button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-shake"> <Icon name="bell" /> Notifications </button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-rotate"> <Icon name="settings" /> Settings </button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-pulse"> <Icon name="heart" /> Love </button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-rotate"> <Icon name="x" /> Close </button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-tada"> <Icon name="check" /> Confirm </button>
<button type="button" class="btn btn-animate-icon"> Next <Icon name="chevron-right" class="icon-end" /> </button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-move-start"> <Icon name="chevron-left" /> Previous </button>
</div>
</Example>
+2 -2
View File
@@ -158,8 +158,8 @@ function greetUser(name) {
</CardBody>
<CardFooter>
<div class="d-flex">
<a class="btn btn-link">Cancel</a>
<a class="btn btn-primary ms-auto">Save</a>
<button type="button" class="btn btn-link">Cancel</button>
<button type="button" class="btn btn-primary ms-auto">Save</button>
</div>
</CardFooter>
</Card>
+14 -12
View File
@@ -140,18 +140,20 @@ const invoiceStatus = {
<div class="form-selectgroup form-selectgroup-boxes d-flex flex-column">
{
paymentMethods.map((method) => (
<label class="form-selectgroup-item flex-fill">
<input type="radio" name="payment-method" value={method.provider} class="form-selectgroup-input" checked={method.isDefault} />
<div class="form-selectgroup-item flex-fill">
<input type="radio" id={`payment-method-${method.provider}`} name="payment-method" value={method.provider} class="form-selectgroup-input" checked={method.isDefault} />
<div class="form-selectgroup-label d-flex align-items-center p-3">
<div class="me-3">
<span class="form-selectgroup-check" />
</div>
<Payment payment={method.provider} size="sm" class="me-3" />
<div class="me-auto">
<div class="fw-medium">{method.title}</div>
<div class="text-secondary">{method.description}</div>
</div>
{method.isDefault && <Badge text="Default" color="primary" light class="me-2" />}
<label for={`payment-method-${method.provider}`} class="d-flex align-items-center flex-fill mb-0">
<div class="me-3">
<span class="form-selectgroup-check" />
</div>
<Payment payment={method.provider} size="sm" class="me-3" />
<div class="me-auto">
<div class="fw-medium">{method.title}</div>
<div class="text-secondary">{method.description}</div>
</div>
{method.isDefault && <Badge text="Default" color="primary" light class="me-2" />}
</label>
<div class="dropdown">
<button type="button" class="btn btn-action btn-sm" data-bs-toggle="dropdown" aria-expanded="false" aria-label={`Actions for ${method.title}`}>
<Icon name="dots-vertical" />
@@ -174,7 +176,7 @@ const invoiceStatus = {
</div>
</div>
</div>
</label>
</div>
))
}
</div>
+50 -42
View File
@@ -46,18 +46,18 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<CardTitle>Shapes</CardTitle>
<CardSubtitle>Round the corners with <code>.btn-pill</code>, or square them off with <code>.btn-square</code>.</CardSubtitle>
<ButtonList>
<a class="btn">Default</a>
<a class="btn btn-pill">Pill</a>
<a class="btn btn-square">Square</a>
<a class="btn btn-icon">
<button type="button" class="btn">Default</button>
<button type="button" class="btn btn-pill">Pill</button>
<button type="button" class="btn btn-square">Square</button>
<button type="button" class="btn btn-icon">
<Icon name="star" />
</a>
<a class="btn btn-pill btn-icon">
</button>
<button type="button" class="btn btn-pill btn-icon">
<Icon name="star" />
</a>
<a class="btn btn-square btn-icon">
</button>
<button type="button" class="btn btn-square btn-icon">
<Icon name="star" />
</a>
</button>
</ButtonList>
</CardBody>
</Card>
@@ -105,33 +105,33 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<CardTitle>Animated icon</CardTitle>
<CardSubtitle>Small hover animations draw attention to the icon.</CardSubtitle>
<ButtonList>
<a class="btn btn-animate-icon">
<button type="button" class="btn btn-animate-icon">
Save <Icon name="arrow-right" class="icon-end" />
</a>
<a class="btn btn-animate-icon btn-animate-icon-rotate">
</button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-rotate">
<Icon name="plus" /> Add
</a>
<a class="btn btn-animate-icon btn-animate-icon-shake">
</button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-shake">
<Icon name="bell" /> Notifications
</a>
<a class="btn btn-animate-icon btn-animate-icon-rotate">
</button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-rotate">
<Icon name="settings" /> Settings
</a>
<a class="btn btn-animate-icon btn-animate-icon-pulse">
</button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-pulse">
<Icon name="heart" /> Love
</a>
<a class="btn btn-animate-icon btn-animate-icon-rotate">
</button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-rotate">
<Icon name="x" /> Close
</a>
<a class="btn btn-animate-icon btn-animate-icon-tada">
</button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-tada">
<Icon name="check" /> Confirm
</a>
<a class="btn btn-animate-icon">
</button>
<button type="button" class="btn btn-animate-icon">
Next <Icon name="chevron-right" class="icon-end" />
</a>
<a class="btn btn-animate-icon btn-animate-icon-move-start">
</button>
<button type="button" class="btn btn-animate-icon btn-animate-icon-move-start">
<Icon name="chevron-left" /> Previous
</a>
</button>
</ButtonList>
</CardBody>
</Card>
@@ -144,9 +144,9 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<ButtonList>
{
themeColors.map(([name, color]) => (
<a class={`btn btn-${name}`}>
<button type="button" class={`btn btn-${name}`}>
{color.icon && <Icon name={color.icon} />} {color.title}
</a>
</button>
))
}
</ButtonList>
@@ -161,9 +161,9 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<ButtonList>
{
themeColors.map(([name, color]) => (
<a class={`btn btn-outline btn-${name}`}>
<button type="button" class={`btn btn-outline btn-${name}`}>
{color.icon && <Icon name={color.icon} />} {color.title}
</a>
</button>
))
}
</ButtonList>
@@ -178,9 +178,9 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<ButtonList>
{
themeColors.map(([name, color]) => (
<a class={`btn btn-ghost btn-${name}`}>
<button type="button" class={`btn btn-ghost btn-${name}`}>
{color.icon && <Icon name={color.icon} />} {color.title}
</a>
</button>
))
}
</ButtonList>
@@ -195,9 +195,9 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<ButtonList>
{
themeColors.map(([name, color]) => (
<a class={`btn btn-pill btn-${name}`}>
<button type="button" class={`btn btn-pill btn-${name}`}>
{color.icon && <Icon name={color.icon} />} {color.title}
</a>
</button>
))
}
</ButtonList>
@@ -212,9 +212,9 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<ButtonList>
{
themeColors.map(([name, color]) => (
<a class={`btn btn-square btn-${name}`}>
<button type="button" class={`btn btn-square btn-${name}`}>
{color.icon && <Icon name={color.icon} />} {color.title}
</a>
</button>
))
}
</ButtonList>
@@ -229,9 +229,9 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<ButtonList>
{
colors.map(([name, color]) => (
<a class={`btn btn-${name}`}>
<button type="button" class={`btn btn-${name}`}>
{color.icon && <Icon name={color.icon} />} {color.title}
</a>
</button>
))
}
</ButtonList>
@@ -246,9 +246,9 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<ButtonList>
{
socialColors.map(([name, app]) => (
<a class={`btn btn-${name}`}>
<button type="button" class={`btn btn-${name}`}>
{app.icon && <Icon name={app.icon} />} {app.title}
</a>
</button>
))
}
</ButtonList>
@@ -260,7 +260,15 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
<CardBody>
<CardTitle>Icon buttons</CardTitle>
<CardSubtitle>Social brand colors without a label, for compact toolbars.</CardSubtitle>
<ButtonList>{socialColors.map(([name, app]) => <a class={`btn btn-icon btn-${name}`}>{app.icon && <Icon name={app.icon} />}</a>)}</ButtonList>
<ButtonList
>{
socialColors.map(([name, app]) => (
<button type="button" class={`btn btn-icon btn-${name}`}>
{app.icon && <Icon name={app.icon} />}
</button>
))
}</ButtonList
>
</CardBody>
</Card>
</div>
+9 -9
View File
@@ -232,8 +232,8 @@ const operationCurrencies = currencies.slice(0, 20)
<p>Place new order:</p>
<div class="input-group mb-3">
<label class="input-group-text">Amount</label>
<select class="form-select">
<label class="input-group-text" for="crypto-amount-value">Amount</label>
<select class="form-select" aria-label="Currency">
{
operationCurrencies.map((currency, i) => (
<option value={currency.symbol} selected={i === 0}>
@@ -242,19 +242,19 @@ const operationCurrencies = currencies.slice(0, 20)
))
}
</select>
<input type="text" class="form-control" value="0.25" />
<input type="text" id="crypto-amount-value" class="form-control" value="0.25" />
</div>
<div class="input-group mb-3">
<label class="input-group-text">Price</label>
<input type="text" class="form-control" readonly="" value="23,077.05" />
<label class="input-group-text">$</label>
<label class="input-group-text" for="crypto-order-price">Price</label>
<input type="text" id="crypto-order-price" class="form-control" readonly="" value="23,077.05" />
<label class="input-group-text" for="crypto-order-price">$</label>
</div>
<div class="input-group mb-3">
<label class="input-group-text">Total</label>
<input type="text" class="form-control" readonly="" value="5,769.27" />
<label class="input-group-text">$</label>
<label class="input-group-text" for="crypto-order-total">Total</label>
<input type="text" id="crypto-order-total" class="form-control" readonly="" value="5,769.27" />
<label class="input-group-text" for="crypto-order-total">$</label>
</div>
<div class="d-grid">
+1 -1
View File
@@ -57,7 +57,7 @@ const emailEntries = Object.entries(emails)
<div class="row g-0">
<div class="col-6">
<div class="p-6 bg-surface-secondary rounded-start">
<img src="" class="img-fluid rounded-start" data-email-image />
<img class="img-fluid rounded-start" alt="" data-email-image />
</div>
</div>
<div class="col-6">
+4 -4
View File
@@ -239,7 +239,7 @@ const rows = [
rows.map((row) => (
<tr>
<td>
<select class="form-select">
<select class="form-select" aria-label="Source">
{sources.map(([v, l]) => (
<option value={v} selected={v === row.source || undefined}>
{l}
@@ -248,10 +248,10 @@ const rows = [
</select>
</td>
<td>
<input type="text" class="form-control" value={row.prop || undefined} />
<input type="text" class="form-control" aria-label="Property" value={row.prop || undefined} />
</td>
<td>
<select class="form-select">
<select class="form-select" aria-label="Comparison">
{comparisons.map(([v, l]) => (
<option value={v} selected={v === row.comparison || undefined}>
{l}
@@ -260,7 +260,7 @@ const rows = [
</select>
</td>
<td>
<input type="text" class="form-control" value={row.target} />
<input type="text" class="form-control" aria-label="Target" value={row.target} />
</td>
</tr>
))
+1 -3
View File
@@ -2,14 +2,12 @@
import SingleLayout from '@shared/layouts/SingleLayout.astro'
import CardTitle from '@ui/CardTitle.astro'
import Prose from '@ui/Prose.astro'
// Card title is empty on purpose.
---
<SingleLayout title="Terms of service" containerSize="narrow">
<div class="card card-md">
<div class="card-body">
<CardTitle />
<CardTitle>Terms of service</CardTitle>
<Prose>
<p>
We (the folks at Tabler) run a website hosting platform called <a href="https://tabler.io">tabler.io</a> and would love for you to use it. Tabler.io's basic service is free, and we offer paid upgrades for advanced features. Our service is designed to give you as much control and ownership over what goes on your
+2 -2
View File
@@ -142,11 +142,11 @@ const imgBottomPhoto = requireIndex(photos as { file: string; title: string }[],
<div class={`col-auto${elRight ? ' ms-auto' : ''}`}>
{el === 'switch' ? (
<label class="form-check form-switch m-0">
<input class="form-check-input position-static" type="checkbox" checked />
<input class="form-check-input position-static" type="checkbox" checked aria-label="Enable" />
</label>
) : el === 'check' ? (
<label class="form-check m-0">
<input class="form-check-input position-static" type="checkbox" checked />
<input class="form-check-input position-static" type="checkbox" checked aria-label="Select" />
</label>
) : el === 'avatars' ? (
<PeopleAvatarList stacked={true} size="sm" text="+3" />
+1 -1
View File
@@ -55,7 +55,7 @@ const paneClass = `tab-pane${animation ? ' fade' : ''}`
{
dropdown && (
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
Dropdown
</a>
<DropdownMenu />
+1 -1
View File
@@ -59,7 +59,7 @@ const rows = people.slice(offset, offset + limit).map((person, idx) => {
</div>
)}
<div class="col-auto">
<a href="#">
<a href="#" tabindex="-1" aria-hidden="true">
<Avatar person={row.person} />
</a>
</div>
@@ -41,7 +41,7 @@ const rows = sorted.map((person, idx) => {
<ListGroupItem>
<div class="row">
<div class="col-auto">
<a href="#">
<a href="#" tabindex="-1" aria-hidden="true">
<Avatar person={row.person} />
</a>
</div>
+1 -1
View File
@@ -36,7 +36,7 @@ import Progress from '@ui/Progress.astro'
</div>
</div>
<div class="col-12 col-sm-6 d-flex justify-content-center">
<a href="./illustrations.html">
<a href="./illustrations.html" aria-label="Illustrations">
<svg xmlns="http://www.w3.org/2000/svg" height="200" fill="none" viewBox="0 0 800 600">
<style is:inline>
:where(.theme-dark, [data-theme='dark']) .tblr-illustrations-good-info-a {
+1 -1
View File
@@ -28,7 +28,7 @@ const classes = ['nav', pills ? 'nav-pills' : tabs ? 'nav-tabs' : undefined, cla
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true"> Disabled </a>
</li>
<li class="nav-item ms-auto">
<a class="nav-link" href="#">
<a class="nav-link" href="#" aria-label="Settings">
<Icon name="settings" />
</a>
</li>
+4 -8
View File
@@ -256,13 +256,11 @@ import Avatar from '@ui/Avatar.astro'
<input type="radio" class="btn-check" name="btn-radio-dropdown" id="btn-radio-dropdown-2" autocomplete="off" />
<label for="btn-radio-dropdown-2" {...{ type: 'button' } as unknown as astroHTML.JSX.LabelHTMLAttributes} class="btn">Option 2</label>
<ButtonGroup>
<input type="radio" class="btn-check" name="btn-radio-dropdown" id="btn-radio-dropdown-dropdown" autocomplete="off" />
<label for="btn-radio-dropdown-dropdown" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Other </label>
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Other </button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"> Action </a>
<a class="dropdown-item" href="#"> Another action </a>
</div>
<div class="dropdown-menu dropdown-menu-end">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#"> Option 4 </a>
<a class="dropdown-item" href="#"> Option 5 </a>
<a class="dropdown-item" href="#"> Option 6 </a>
@@ -306,13 +304,11 @@ import Avatar from '@ui/Avatar.astro'
<input type="radio" class="btn-check" name="btn-radio-vertical-dropdown" id="btn-radio-vertical-dropdown-4" autocomplete="off" />
<label for="btn-radio-vertical-dropdown-4" {...{ type: 'button' } as unknown as astroHTML.JSX.LabelHTMLAttributes} class="btn">Button 4</label>
<ButtonGroup>
<input type="radio" class="btn-check" name="btn-radio-vertical-dropdown" id="btn-radio-vertical-dropdown-dropdown" autocomplete="off" />
<label for="btn-radio-vertical-dropdown-dropdown" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Other </label>
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Other </button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"> Action </a>
<a class="dropdown-item" href="#"> Another action </a>
</div>
<div class="dropdown-menu dropdown-menu-end">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#"> Option 4 </a>
<a class="dropdown-item" href="#"> Option 5 </a>
<a class="dropdown-item" href="#"> Option 6 </a>
@@ -7,7 +7,7 @@ import Button from '@ui/Button.astro'
<div class="d-flex">
<div class="me-3">
<div class="input-icon">
<input type="text" value="" class="form-control" placeholder="Search…" />
<input type="text" value="" class="form-control" placeholder="Search…" aria-label="Search" />
<span class="input-icon-addon">
<Icon name="search" />
@@ -4,7 +4,7 @@ import Button from '@ui/Button.astro'
<!-- BEGIN HEADER ACTIONS -->
<div class="d-flex">
<input type="search" class="form-control d-inline-block w-9 me-3" placeholder="Search user…" />
<input type="search" class="form-control d-inline-block w-9 me-3" placeholder="Search user…" aria-label="Search user" />
<Button text="New user" color="primary" icon="plus" />
</div>
<!-- END HEADER ACTIONS -->
@@ -26,13 +26,13 @@ const sectionClass = ['section', background && `section-${background}`, classNam
<h2 class="section-title text-secondary">Trusted by over 3,000 companies</h2>
</div>
<div class="row g-lg-8 align-items-center justify-content-center">
<div class="col-auto"><a href="#" class="link-secondary"><Baremetrics /></a></div>
<div class="col-auto"><a href="#" class="link-secondary"><Cgi /></a></div>
<div class="col-auto"><a href="#" class="link-secondary"><Docplanner /></a></div>
<div class="col-auto"><a href="#" class="link-secondary"><Flow /></a></div>
<div class="col-auto"><a href="#" class="link-secondary"><Fubotv /></a></div>
<div class="col-auto"><a href="#" class="link-secondary"><SixFlags /></a></div>
<div class="col-auto"><a href="#" class="link-secondary"><Vocus /></a></div>
<div class="col-auto"><a href="#" class="link-secondary" aria-label="Baremetrics"><Baremetrics /></a></div>
<div class="col-auto"><a href="#" class="link-secondary" aria-label="CGI"><Cgi /></a></div>
<div class="col-auto"><a href="#" class="link-secondary" aria-label="Docplanner"><Docplanner /></a></div>
<div class="col-auto"><a href="#" class="link-secondary" aria-label="Flow"><Flow /></a></div>
<div class="col-auto"><a href="#" class="link-secondary" aria-label="fuboTV"><Fubotv /></a></div>
<div class="col-auto"><a href="#" class="link-secondary" aria-label="Six Flags"><SixFlags /></a></div>
<div class="col-auto"><a href="#" class="link-secondary" aria-label="Vocus"><Vocus /></a></div>
</div>
</div>
</section>
@@ -19,13 +19,13 @@ const sectionClass = ['section', className]
</div>
<div class="container-tight">
<div class="row">
<form class="row" action="./" method="get" novalidate>
<div class="col">
<input class="w-100 form-control" placeholder="Your Email" type="email" />
<input class="w-100 form-control" name="email" placeholder="Your Email" type="email" />
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary"><Icon name="mail" /> Subscribe</button>
</div>
</div>
</form>
</div>
</section>
@@ -12,8 +12,8 @@ import InputColor from '../parts/form/InputColor.astro'
<AvatarUpload class="rounded" size="xl" />
</div>
<div class="col">
<label class="form-label">Name</label>
<input type="text" class="form-control" />
<label class="form-label" for="team-name">Name</label>
<input type="text" id="team-name" class="form-control" />
</div>
</div>
+54 -54
View File
@@ -8,16 +8,16 @@
"heightFull": 2370
},
"access-token": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template about generating an access token",
"descriptionLong": "Allows to inform the user that a new access token has been created for their account. Shows when the token was generated and from which device or location. Sends directly to the security settings by the button to revoke it if not recognized.",
"width": 1248,
"height": 1032,
"widthFull": 1440,
"heightFull": 1786
},
"account-deleted": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template confirming an account deletion",
"descriptionLong": "Allows to confirm that a user's account and its data have been permanently deleted. Shows the date the account was removed and a short summary of what happens next. Gives a contact link in case the deletion was not requested.",
"width": 1248,
"height": 1152,
"widthFull": 1440,
@@ -40,16 +40,16 @@
"heightFull": 3260
},
"calendar": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with a calendar of events",
"descriptionLong": "Allows to show a list of upcoming events over the month, each with its date and short description. Highlights the closest event separately. Refers to the full calendar on the website by the button.",
"width": 1248,
"height": 2744,
"widthFull": 1440,
"heightFull": 3498
},
"changelog": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with a product changelog",
"descriptionLong": "Allows to list the latest changes, fixes and new features added to the product. Groups items by category and shows the release date. Sends to the full changelog page by the button.",
"width": 1248,
"height": 1956,
"widthFull": 1440,
@@ -64,8 +64,8 @@
"heightFull": 3528
},
"company-email": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template for internal company communication",
"descriptionLong": "Allows to share company news and announcements with employees. Shows the sender's name and department along with the message content. Includes a button to read the full announcement on the intranet.",
"width": 1248,
"height": 1604,
"widthFull": 1440,
@@ -80,8 +80,8 @@
"heightFull": 4440
},
"confirm-email": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template to confirm a new email address",
"descriptionLong": "Allows to verify that a newly added email address belongs to the user. Shows the address to be confirmed and explains why confirmation is needed. Sends directly to the confirmation page by the button.",
"width": 1248,
"height": 1484,
"widthFull": 1440,
@@ -96,16 +96,16 @@
"heightFull": 5204
},
"deployment-failed": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template about a failed deployment",
"descriptionLong": "Allows to notify the team that a deployment did not complete successfully. Shows the project name, the failed step and the time it happened. Sends to the build logs by the button to investigate the cause.",
"width": 1248,
"height": 1906,
"widthFull": 1440,
"heightFull": 2660
},
"domain-configuration": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template about domain configuration",
"descriptionLong": "Allows to guide the user through connecting a custom domain. Shows the required DNS records and their current verification status. Sends to the domain settings by the button to finish the setup.",
"width": 1248,
"height": 1474,
"widthFull": 1440,
@@ -168,40 +168,40 @@
"heightFull": 3406
},
"features-3": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template presenting product features",
"descriptionLong": "Allows to showcase a set of product features with an icon and short description for each. Groups the features so they are easy to scan. Sends to the features page by the button for more details.",
"width": 1248,
"height": 1340,
"widthFull": 1440,
"heightFull": 2094
},
"footer-1": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email footer template with social links",
"descriptionLong": "Shows a simple footer block with the company logo, social media icons and an unsubscribe link. Fits at the bottom of any email template. Keeps the layout compact and consistent with the rest of the message.",
"width": 1248,
"height": 244,
"widthFull": 1440,
"heightFull": 998
},
"footer-2": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email footer template with contact details",
"descriptionLong": "Shows a footer block with the company address, phone number and support email. Includes links to the privacy policy and unsubscribe page. Fits at the bottom of any email template.",
"width": 1248,
"height": 244,
"widthFull": 1440,
"heightFull": 964
},
"footer-3": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email footer template with navigation links",
"descriptionLong": "Shows a footer block with links to the main sections of the website. Includes the company logo and copyright notice. Fits at the bottom of any email template.",
"width": 1248,
"height": 244,
"widthFull": 1440,
"heightFull": 1222
},
"footer-4": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email footer template with app download links",
"descriptionLong": "Shows a footer block promoting the mobile app with App Store and Google Play buttons. Includes the company logo and an unsubscribe link. Fits at the bottom of any email template.",
"width": 1248,
"height": 244,
"widthFull": 1440,
@@ -240,8 +240,8 @@
"heightFull": 2048
},
"invitation-2": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with an invitation to collaborate",
"descriptionLong": "Shows who invites the user and to which project or team. Explains what access will be granted once the invitation is accepted. Accepts or declines the invitation by the button at any time.",
"width": 1248,
"height": 1652,
"widthFull": 1440,
@@ -272,8 +272,8 @@
"heightFull": 2484
},
"magic-link": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with a magic link to sign in",
"descriptionLong": "Allows to sign in without a password by sending a one-time link to the user's email. Shows how long the link stays valid before it expires. Sends directly to the account by the button.",
"width": 1248,
"height": 1180,
"widthFull": 1440,
@@ -296,16 +296,16 @@
"heightFull": 2530
},
"new-app": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template announcing a new app",
"descriptionLong": "Allows to introduce a newly released application to users. Shows a short description of what the app does along with a preview image. Sends to the download page by the button.",
"width": 1248,
"height": 1270,
"widthFull": 1440,
"heightFull": 2024
},
"new-photos": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template about new photos added",
"descriptionLong": "Allows to notify the user that new photos have been added to an album or gallery. Shows a preview of the latest photos along with who added them. Sends to the full gallery by the button.",
"width": 1248,
"height": 1768,
"widthFull": 1440,
@@ -320,8 +320,8 @@
"heightFull": 6044
},
"off-deals": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with discounted deals",
"descriptionLong": "Allows to present a list of products currently on sale with their discounted price. Shows the discount percentage and how long the deal lasts. Sends directly to the store by the button.",
"width": 1248,
"height": 2508,
"widthFull": 1440,
@@ -344,8 +344,8 @@
"heightFull": 4442
},
"otp-code": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with a one-time password code",
"descriptionLong": "Allows to send a one-time code used to verify the user's identity. Shows how long the code stays valid before it expires. Reminds the user not to share the code with anyone.",
"width": 1248,
"height": 1908,
"widthFull": 1440,
@@ -376,8 +376,8 @@
"heightFull": 3950
},
"post": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template about a new post",
"descriptionLong": "Allows to notify followers when a new post is published. Shows the title, a short excerpt and the cover image of the post. Sends to the full post by the button.",
"width": 1248,
"height": 1504,
"widthFull": 1440,
@@ -424,16 +424,16 @@
"heightFull": 2096
},
"promotions": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with a list of promotions",
"descriptionLong": "Allows to present several ongoing promotions in a single email. Shows each offer with its own image, description and expiry date. Sends directly to the store by the button for each offer.",
"width": 1248,
"height": 3058,
"widthFull": 1440,
"heightFull": 3812
},
"promotions-2": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with a featured promotion",
"descriptionLong": "Allows to highlight one main promotion with a large image and short description. Shows the discount and the expiry date of the offer. Sends directly to the store by the button.",
"width": 1248,
"height": 1626,
"widthFull": 1440,
@@ -464,8 +464,8 @@
"heightFull": 3358
},
"review": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template asking for a review",
"descriptionLong": "Allows to ask the user to leave a review after a purchase or an interaction. Shows the product or service along with a star rating widget. Sends to the review form by the button.",
"width": 1248,
"height": 1340,
"widthFull": 1440,
@@ -488,8 +488,8 @@
"heightFull": 2522
},
"security-alert": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with a security alert",
"descriptionLong": "Allows to warn the user about a suspicious sign-in attempt or account activity. Shows the device, location and time of the activity. Sends directly to the security settings by the button to secure the account.",
"width": 1248,
"height": 1272,
"widthFull": 1440,
@@ -544,8 +544,8 @@
"heightFull": 2750
},
"survey-emoji": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with an emoji survey",
"descriptionLong": "Allows to collect quick feedback using a row of emoji ratings. Shows a short question above the emoji options. Sends the response directly by clicking on an emoji.",
"width": 1248,
"height": 1834,
"widthFull": 1440,
@@ -592,8 +592,8 @@
"heightFull": 3114
},
"uptime-report": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template with an uptime report",
"descriptionLong": "Allows to summarize the uptime and incidents of a monitored service over a period. Shows the uptime percentage along with a short list of incidents. Sends to the full report by the button.",
"width": 1248,
"height": 1750,
"widthFull": 1440,
@@ -624,8 +624,8 @@
"heightFull": 3680
},
"welcome": {
"descriptionShort": "",
"descriptionLong": "",
"descriptionShort": "Email template welcoming a new user",
"descriptionLong": "Allows to greet a new user right after they sign up. Shows a short introduction to the product along with the first steps to get started. Sends to the dashboard by the button.",
"width": 1248,
"height": 1484,
"widthFull": 1440,
+1 -1
View File
@@ -130,7 +130,7 @@ const base = '..'
<div class="col-auto">
<ul class="list-inline list-inline-dots">
<li class="list-inline-item">
<a class="link-secondary" href="/terms-of-service">Terms of service</a>
<a class="link-secondary" href="/terms-of-service.html">Terms of service</a>
</li>
<li class="list-inline-item">
<a class="link-secondary" href="#">Privacy policy</a>
+1 -1
View File
@@ -66,5 +66,5 @@ const classes = ['alert', variant && `alert-${variant}`, `alert-${color}`, showC
</Fragment>
)
}
{showClose && <a class="btn-close" data-bs-dismiss="alert" aria-label="close" />}
{showClose && <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close" />}
</div>
+5 -2
View File
@@ -15,6 +15,8 @@ interface Props {
/** person object (photo → src, initials → placeholder); resolve demo ids with personById() from @shared/lib/people */
person?: Person | undefined
link?: boolean
/** href for the linked avatar (link); defaults to "#" when omitted */
href?: string
size?: AvatarSize | undefined
class?: string
/** avatar-{shape} (core/scss/ui/_avatars.scss) */
@@ -30,7 +32,7 @@ interface Props {
[key: string]: unknown
}
const { src: srcProp, placeholder: placeholderProp, person, link: linkProp = false, size, class: className, shape, color, status, statusLabel, brand, icon, base = assetRoot, ...rest } = Astro.props
const { src: srcProp, placeholder: placeholderProp, person, link: linkProp = false, href: hrefProp, size, class: className, shape, color, status, statusLabel, brand, icon, base = assetRoot, ...rest } = Astro.props
let src = srcProp
let placeholder = placeholderProp
@@ -43,10 +45,11 @@ if (person) {
}
const El = linkProp ? 'a' : 'span'
const linkAttrs = linkProp ? { href: hrefProp ?? '#' } : {}
const classes = ['avatar', size && `avatar-${size}`, className, shape && `avatar-${shape}`, color && `bg-${color}-lt`]
---
<El class:list={classes} style={src ? `background-image: url(${base}/${src})` : undefined} {...rest}
<El {...linkAttrs} class:list={classes} style={src ? `background-image: url(${base}/${src})` : undefined} {...rest}
>{status && <span class={`badge bg-${status}`} aria-label={statusLabel ?? status} />}{brand && <span class="avatar-brand" style={`background-image: url(${base}/static/brands/${brand}.svg);`} />}{src ? null : placeholder ? placeholder : icon ? <Icon name={icon} /> : <Icon name="user" />}</El
>
+4 -2
View File
@@ -9,9 +9,11 @@ interface Props {
color?: string
size?: IconSize | undefined
value?: number
/** accessible name for the underlying select; defaults to "Rating" */
label?: string
}
const { id, icon = 'star', color, size, value } = Astro.props
const { id, icon = 'star', color, size, value, label = 'Rating' } = Astro.props
const starClasses = ['icon', 'gl-star-full', color && `text-${color}`, size && `icon-${size}`].filter(Boolean).join(' ')
@@ -21,7 +23,7 @@ const ratingSelector = `#rating-${id}`
const ratingKey = `rating-${id}`
---
<select id={`rating-${id}`}>
<select id={`rating-${id}`} aria-label={label}>
<option value="">Select a rating</option>
<option value="5" selected={value === 5}>Excellent</option>
<option value="4" selected={value === 4}>Very Good</option>
+1 -1
View File
@@ -37,7 +37,7 @@ const { text, flag, icon, person, status, legend, checkbox, checked, badge, clas
) : legend ? (
<span class={`legend-dot bg-${legend}`} />
) : checkbox ? (
<input type="checkbox" class="form-check-input tag-check" checked={checked} />
<input type="checkbox" class="form-check-input tag-check" checked={checked} aria-label={text} />
) : null
}
{text}
+1 -3
View File
@@ -8,9 +8,7 @@ const { id } = Astro.props
const selector = `#hugerte-${id}`
---
<form method="post">
<textarea id={`hugerte-${id}`} set:html={'Hello, <b>Tabler</b>!'} />
</form>
<textarea id={`hugerte-${id}`} set:html={'Hello, <b>Tabler</b>!'} />
<CaptureScript>
<!-- BEGIN WYSIWYG -->
<script is:inline define:vars={{ selector }}>