Redesign Interface demo pages for clarity and consistency (#2863)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bartosz-DoandClaude Sonnet 5 authored and GitHub committed 2026-08-12 23:55:36 +02:00
1 parent 464a52255b
commit 5363668dc2
25 files changed
+798 -263

No files matched your search

@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Made Interface demo pages consistent with the Buttons page: card subtitles, one docs link, responsive grids.
+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": minor
---
Added the missing Prose demo page, linked from the menu but not previously built.
+27 -1
View File
@@ -3,25 +3,45 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Accordion from '@ui/Accordion.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const accordionDocsUrl = getDocsUrl('/ui/components/accordion')
---
<DefaultLayout title="Accordion" pageHeader="Accordion" pageMenu="base.accordion">
<DefaultLayout title="Accordion" pageMenu="base.accordion" description="Accordions show and hide sections of content, one panel at a time, inside a card.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Accordion</h1>
<a href={accordionDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Default</CardTitle>
<div class="card-subtitle">The base accordion — one panel open at a time.</div>
<Accordion />
</CardBody>
</Card>
</div>
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Flush</CardTitle>
<div class="card-subtitle">Add <code>flush</code> to remove the card border and rounded corners.</div>
</CardBody>
<Accordion type={['flush']} id="flush" />
</Card>
</div>
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Tabs</CardTitle>
<div class="card-subtitle">Add <code>tabs</code> for a tab-like header instead of the default style.</div>
<Accordion type={['tabs']} id="tabs" />
</CardBody>
</Card>
@@ -29,6 +49,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Inverted</CardTitle>
<div class="card-subtitle">Add <code>inverted</code> to flip the toggle icon to the start of the header.</div>
<Accordion type={['inverted']} id="inverted" />
</CardBody>
</Card>
@@ -36,6 +58,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Inverted with plus icon</CardTitle>
<div class="card-subtitle">Combine <code>inverted</code> with a custom <code>toggleIcon</code>.</div>
<Accordion id="inverted-plus" type={['inverted', 'plus']} toggleIcon="plus" />
</CardBody>
</Card>
@@ -43,6 +67,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>With icons</CardTitle>
<div class="card-subtitle">Add <code>showIcon</code> to show an icon next to each panel's title.</div>
<Accordion id="icons" showIcon />
</CardBody>
</Card>
+30 -13
View File
@@ -4,14 +4,26 @@ import Alert from '@ui/Alert.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const alertDocsUrl = getDocsUrl('/ui/components/alert')
---
<DefaultLayout title="Alerts" pageHeader="Alerts" pageMenu="base.alerts">
<DefaultLayout title="Alerts" pageMenu="base.alerts" description="Alert messages inform users about the status of an action, in success, info, warning, or danger states.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Alerts</h1>
<a href={alertDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-lg-6">
<div class="col-12">
<Card>
<CardBody>
<CardTitle as="h2">Basic alerts</CardTitle>
<CardTitle as="h2">Basic</CardTitle>
<div class="card-subtitle">A title and a color are enough to show the status of an action.</div>
<Alert type="danger" title="An error occurred!" />
<Alert type="warning" title="Some information is missing!" />
<Alert type="success" title="Completed successfully!" />
@@ -19,10 +31,11 @@ import CardTitle from '@ui/CardTitle.astro'
</CardBody>
</Card>
</div>
<div class="col-lg-6">
<div class="col-12">
<Card>
<CardBody>
<CardTitle as="h2">Alerts with action</CardTitle>
<CardTitle as="h2">With action</CardTitle>
<div class="card-subtitle">Add a link with the <code>action</code> prop to give users something to do next.</div>
<Alert showClose action="Link" type="danger" title="An error occurred!" />
<Alert showClose action="Link" type="warning" title="Some information is missing!" />
<Alert showClose action="Link" type="success" title="Completed successfully!" />
@@ -30,10 +43,11 @@ import CardTitle from '@ui/CardTitle.astro'
</CardBody>
</Card>
</div>
<div class="col-lg-6">
<div class="col-12">
<Card>
<CardBody>
<CardTitle as="h2">Dismissible alerts</CardTitle>
<CardTitle as="h2">Dismissible</CardTitle>
<div class="card-subtitle">Add <code>showClose</code> to let users close the alert.</div>
<Alert showClose type="danger" title="An error occurred!" />
<Alert showClose type="warning" title="Some information is missing!" />
<Alert showClose type="success" title="Completed successfully!" />
@@ -41,10 +55,11 @@ import CardTitle from '@ui/CardTitle.astro'
</CardBody>
</Card>
</div>
<div class="col-lg-6">
<div class="col-12">
<Card>
<CardBody>
<CardTitle as="h2">Alert with a description</CardTitle>
<CardTitle as="h2">With a description</CardTitle>
<div class="card-subtitle">Add a <code>description</code> or a <code>list</code> when the title alone isn't enough context.</div>
<Alert showClose type="danger" title="Password does not meet requirements:" list={['Minimum 8 characters', 'Include a special character']} />
<Alert showClose type="warning" title="Some information is missing!" description="This is a custom alert box with a description." />
<Alert showClose type="success" title="Completed successfully!" description="This is a custom alert box with a description." />
@@ -52,10 +67,11 @@ import CardTitle from '@ui/CardTitle.astro'
</CardBody>
</Card>
</div>
<div class="col-lg-6">
<div class="col-12">
<Card>
<CardBody>
<CardTitle as="h2">Important alerts</CardTitle>
<CardTitle as="h2">Important</CardTitle>
<div class="card-subtitle">Add <code>important</code> for a bolder style that stands out from the rest of the page.</div>
<Alert showClose important type="danger" title="Password does not meet requirements:" list={['Minimum 8 characters', 'Include a special character']} />
<Alert showClose important type="success" description="This is a custom alert box with a description." />
<Alert showClose important type="warning" description="This is a custom alert box with a description." />
@@ -63,10 +79,11 @@ import CardTitle from '@ui/CardTitle.astro'
</CardBody>
</Card>
</div>
<div class="col-lg-6">
<div class="col-12">
<Card>
<CardBody>
<CardTitle as="h2">Minor alerts</CardTitle>
<CardTitle as="h2">Minor</CardTitle>
<div class="card-subtitle">Add <code>minor</code> for a quieter style that fits inline with other content.</div>
<Alert showClose minor type="danger" title="Password does not meet requirements:" list={['Minimum 8 characters', 'Include a special character']} />
<Alert showClose minor type="success" description="This is a custom alert box with a description." />
<Alert showClose minor type="warning" description="This is a custom alert box with a description." />
+36 -17
View File
@@ -6,9 +6,10 @@ import AvatarList from '@ui/AvatarList.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import people from '@data/people.json'
import { site } from '@shared/lib/site'
import { firstLetters } from '@shared/lib/string-format'
import { firstLetters, getDocsUrl } from '@shared/lib/string-format'
const iconIcons = ['user', 'settings', 'car', 'balloon', 'users', 'users-group', 'apps', 'ghost']
@@ -22,64 +23,77 @@ const listSizes = ['xxs', 'xs', 'sm', 'md', 'lg'] as const
const uploadSizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl'] as const
const statusColors = ['red', 'green', 'blue', 'yellow', 'secondary']
const brands = ['netflix', 'amazon', 'messenger', 'figma', 'twitch']
const avatarDocsUrl = getDocsUrl('/ui/components/avatar')
---
<DefaultLayout title="Avatars" pageMenu="base.avatars" pageHeader="Avatars">
<DefaultLayout title="Avatars" pageMenu="base.avatars" description="Avatars display a photo, icon, or initials to represent a person, brand, or status.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Avatars</h1>
<a href={avatarDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Default avatar</CardTitle>
<div class="card-subtitle">The base <code>.avatar</code> element — a placeholder box for a photo, icon, or initials.</div>
<Avatar />
</CardBody>
</Card>
</div>
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Avatar with icon</CardTitle>
<div class="card-subtitle">Use an icon instead of a photo with the <code>icon</code> prop.</div>
<div class="avatar-list">
{iconIcons.map((icon) => <Avatar icon={icon} />)}
</div>
</CardBody>
</Card>
</div>
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Avatar with icon</CardTitle>
<CardTitle as="h2">Avatar icon colors</CardTitle>
<div class="card-subtitle">Combine an icon avatar with any theme color.</div>
<div class="avatar-list">
{colors.map((color) => <Avatar icon="user" color={color} />)}
</div>
</CardBody>
</Card>
</div>
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Simple avatar</CardTitle>
<div class="card-subtitle">Pass a <code>person</code> object to render their photo.</div>
<div class="avatar-list">
{people8.map((person) => <Avatar person={person} />)}
</div>
</CardBody>
</Card>
</div>
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Avatar placeholder</CardTitle>
<div class="card-subtitle">Fall back to initials with the <code>placeholder</code> prop when there's no photo.</div>
<div class="avatar-list">
{people8.map((person) => <Avatar placeholder={firstLetters(person.full_name)} />)}
</div>
</CardBody>
</Card>
</div>
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Avatar shapes</CardTitle>
<div class="card-subtitle">Square by default — add <code>.rounded-circle</code> or <code>.rounded-0</code> to change the shape.</div>
<div class="avatar-list">
<Avatar />
<Avatar class="rounded-circle" />
@@ -88,10 +102,11 @@ const brands = ['netflix', 'amazon', 'messenger', 'figma', 'twitch']
</CardBody>
</Card>
</div>
<div class="col-6">
<div class="col-12 col-lg-6">
<Card>
<CardBody>
<CardTitle as="h2">Avatar sizes</CardTitle>
<div class="card-subtitle">Six sizes from <code>xxs</code> to <code>xl</code>, for both photos and placeholders.</div>
<div class="row">
<div class="col-6">
<div class="avatar-list">
@@ -107,10 +122,11 @@ const brands = ['netflix', 'amazon', 'messenger', 'figma', 'twitch']
</CardBody>
</Card>
</div>
<div class="col-6">
<div class="col-12 col-lg-6">
<Card>
<CardBody>
<CardTitle as="h2">Avatar lists</CardTitle>
<div class="card-subtitle">Stack avatars with <code>&lt;AvatarList&gt;</code> to show a group at a glance.</div>
<div class="row g-3">
{
listSizes.map((size) => (
@@ -138,26 +154,29 @@ const brands = ['netflix', 'amazon', 'messenger', 'figma', 'twitch']
</CardBody>
</Card>
</div>
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Avatar placeholder</CardTitle>
<CardTitle as="h2">Avatar upload</CardTitle>
<div class="card-subtitle">An upload control styled as an avatar, for profile photo pickers.</div>
{uploadSizes.map((size) => <AvatarUpload size={size} />)}
</CardBody>
</Card>
</div>
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Avatar statuses</CardTitle>
<div class="card-subtitle">Add a <code>status</code> dot to show online, away, or busy state.</div>
{statusColors.map((color, i) => <Avatar personId={i + 1} class="rounded-circle" status={color} />)}
</CardBody>
</Card>
</div>
<div class="col-4">
<div class="col-sm-6 col-lg-4">
<Card>
<CardBody>
<CardTitle as="h2">Avatar brands</CardTitle>
<div class="card-subtitle">Show a recognizable brand icon instead of a person.</div>
{brands.map((brand, i) => <Avatar personId={i + 1} brand={brand} />)}
</CardBody>
</Card>
+201 -181
View File
@@ -10,196 +10,216 @@ import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import DropdownMenu from '@ui/DropdownMenu.astro'
import site from '@data/site.json'
import { ucFirst } from '@shared/lib/string-format'
import { ucFirst, getDocsUrl } from '@shared/lib/string-format'
const colors = ['default', ...Object.keys(site.colors), 'dark', 'light']
const sizes = ['sm', 'md', 'lg']
const badgeDocsUrl = getDocsUrl('/ui/components/badge')
---
<DefaultLayout title="Badges" pageHeader="Badges" pageMenu="base.badges">
<div class="row row-cards">
<div class="col-4">
<div class="row row-cards">
<div class="col-12">
<Card>
<CardBody>
<h1>Example heading <span class="badge">New</span></h1>
<h2>Example heading <span class="badge">New</span></h2>
<h3>Example heading <span class="badge">New</span></h3>
<h4>Example heading <span class="badge">New</span></h4>
<h5>Example heading <span class="badge">New</span></h5>
<h6>Example heading <span class="badge">New</span></h6>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Badge sizes</CardTitle>
<DefaultLayout title="Badges" pageMenu="base.badges" description="Badges highlight a count, status, or short label attached to text, buttons, or menu items.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Badges</h1>
<a href={badgeDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="space-y">
{
sizes.map((size) => (
<BadgeList>
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>Default</span>
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>
<Icon name="check" /> Left icon
</span>
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>
Right icon
<Icon name="arrow-right" />
</span>
<span class={`badge badge-icononly${size !== 'md' ? ` badge-${size}` : ''}`}>
<Icon name="star" type="filled" />
</span>
</BadgeList>
))
}
</div>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Positioned badges</CardTitle>
<ButtonList>
<button type="button" class="btn">Notifications <span class="badge text-bg-secondary ms-2">4</span></button>
<button type="button" class="btn">
Inbox
<span class="badge bg-red badge-notification text-red-fg">
9+
<span class="visually-hidden">unread messages</span>
<div class="row row-cards mb-5">
<div class="col-md-6 col-lg-4">
<Card>
<CardBody>
<CardTitle>In headings</CardTitle>
<div class="card-subtitle">A badge scales with the heading level it sits next to.</div>
<h1>Example heading <span class="badge">New</span></h1>
<h2>Example heading <span class="badge">New</span></h2>
<h3>Example heading <span class="badge">New</span></h3>
<h4>Example heading <span class="badge">New</span></h4>
<h5>Example heading <span class="badge">New</span></h5>
<h6>Example heading <span class="badge">New</span></h6>
</CardBody>
</Card>
</div>
<div class="col-md-6 col-lg-4">
<Card>
<CardBody>
<CardTitle>Sizes</CardTitle>
<div class="card-subtitle">Three sizes, with an optional icon before or after the label.</div>
<div class="space-y">
{
sizes.map((size) => (
<BadgeList>
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>Default</span>
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>
<Icon name="check" /> Left icon
</span>
</button>
<button type="button" class="btn">
Profile
<span class="badge badge-dot bg-red badge-notification"></span>
</button>
<button type="button" class="btn">
Settings
<span class="badge badge-dot bg-red badge-notification badge-blink"></span>
</button>
<button type="button" class="btn btn-icon">
<Icon name="bell" />
<span class="badge badge-dot bg-red badge-notification badge-blink"></span>
</button>
<button type="button" class="btn btn-icon btn-action">
<Icon name="bell" />
<span class="badge badge-dot bg-red badge-notification"></span>
</button>
</ButtonList>
</CardBody>
</Card>
</div>
</div>
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>
Right icon
<Icon name="arrow-right" />
</span>
<span class={`badge badge-icononly${size !== 'md' ? ` badge-${size}` : ''}`}>
<Icon name="star" type="filled" />
</span>
</BadgeList>
))
}
</div>
</CardBody>
</Card>
</div>
<div class="col-8">
<div class="row row-cards">
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Basic badges</CardTitle>
<BadgeList>
{colors.map((color) => <span class={`badge bg-${color} text-${color}-fg`}>{ucFirst(color)}</span>)}
</BadgeList>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Light badges</CardTitle>
<BadgeList>
{colors.map((color) => <span class={`badge bg-${color}-lt`}>{ucFirst(color)}</span>)}
</BadgeList>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Outline badges</CardTitle>
<BadgeList>
{colors.map((color) => <span class={`badge badge-outline text-${color}`}>{ucFirst(color)}</span>)}
</BadgeList>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Badges with icons</CardTitle>
<BadgeList>
{
colors.map((color) => (
<span class={`badge bg-${color} text-${color}-fg`}>
{' '}
<Icon name="star" type="filled" /> {ucFirst(color)}{' '}
</span>
))
}
</BadgeList>
</CardBody>
</Card>
</div>
</div>
<div class="col-md-6 col-lg-4">
<Card>
<CardBody>
<CardTitle>Positioned</CardTitle>
<div class="card-subtitle">Attach a count or a dot to a button with <code>.badge-notification</code>.</div>
<ButtonList>
<button type="button" class="btn">Notifications <span class="badge text-bg-secondary ms-2">4</span></button>
<button type="button" class="btn">
Inbox
<span class="badge bg-red badge-notification text-red-fg">
9+
<span class="visually-hidden">unread messages</span>
</span>
</button>
<button type="button" class="btn">
Profile
<span class="badge badge-dot bg-red badge-notification"></span>
</button>
<button type="button" class="btn">
Settings
<span class="badge badge-dot bg-red badge-notification badge-blink"></span>
</button>
<button type="button" class="btn btn-icon">
<Icon name="bell" />
<span class="badge badge-dot bg-red badge-notification badge-blink"></span>
</button>
<button type="button" class="btn btn-icon btn-action">
<Icon name="bell" />
<span class="badge badge-dot bg-red badge-notification"></span>
</button>
</ButtonList>
</CardBody>
</Card>
</div>
<div class="col-sm-6 col-lg-3"><DropdownMenu show badge arrow /></div>
<div class="col-sm-6 col-lg-9">
<div class="row row-cards">
<div class="col-12">
<Card>
<CardBody>
<ButtonList>
{
colors.map((color, index) => (
<button class="btn">
{ucFirst(color)} badge <span class={`badge bg-${color} text-${color}-fg ms-2`}>{index + 1}</span>
</button>
))
}
</ButtonList>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<ButtonList>
{
colors.map((color, index) => (
<button class="btn position-relative">
{ucFirst(color)} badge <span class={`badge bg-${color} text-${color}-fg badge-notification badge-pill`}>{index + 1}</span>
</button>
))
}
</ButtonList>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<ButtonList>
{
colors.map((color) => (
<button class="btn position-relative">
{ucFirst(color)} badge <span class={`badge bg-${color} badge-notification badge-blink`} />
</button>
))
}
</ButtonList>
</CardBody>
</Card>
</div>
</div>
<div class="col-md-6 col-lg-4">
<Card>
<CardBody>
<CardTitle>In a dropdown</CardTitle>
<div class="card-subtitle">A badge inside a dropdown item.</div>
<DropdownMenu show badge arrow />
</CardBody>
</Card>
</div>
<div class="col-md-6 col-lg-4">
<Card>
<CardBody>
<CardTitle>On buttons</CardTitle>
<div class="card-subtitle">Pair any badge color with a button label.</div>
<ButtonList>
{
colors.map((color, index) => (
<button class="btn">
{ucFirst(color)} badge <span class={`badge bg-${color} text-${color}-fg ms-2`}>{index + 1}</span>
</button>
))
}
</ButtonList>
</CardBody>
</Card>
</div>
<div class="col-md-6 col-lg-4">
<Card>
<CardBody>
<CardTitle>Notification dot</CardTitle>
<div class="card-subtitle">A pill-shaped counter positioned on the corner of a button.</div>
<ButtonList>
{
colors.map((color, index) => (
<button class="btn position-relative">
{ucFirst(color)} badge <span class={`badge bg-${color} text-${color}-fg badge-notification badge-pill`}>{index + 1}</span>
</button>
))
}
</ButtonList>
</CardBody>
</Card>
</div>
<div class="col-md-6 col-lg-4">
<Card>
<CardBody>
<CardTitle>Blinking notification</CardTitle>
<div class="card-subtitle">Add <code>.badge-blink</code> to draw attention to unread activity.</div>
<ButtonList>
{
colors.map((color) => (
<button class="btn position-relative">
{ucFirst(color)} badge <span class={`badge bg-${color} badge-notification badge-blink`} />
</button>
))
}
</ButtonList>
</CardBody>
</Card>
</div>
</div>
<div class="row row-cards">
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Basic</CardTitle>
<div class="card-subtitle">Fill a badge with a theme or extended color.</div>
<BadgeList>
{colors.map((color) => <span class={`badge bg-${color} text-${color}-fg`}>{ucFirst(color)}</span>)}
</BadgeList>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Light</CardTitle>
<div class="card-subtitle">A tinted background for a softer, secondary badge.</div>
<BadgeList>
{colors.map((color) => <span class={`badge bg-${color}-lt`}>{ucFirst(color)}</span>)}
</BadgeList>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Outline</CardTitle>
<div class="card-subtitle">A colored border with a transparent background.</div>
<BadgeList>
{colors.map((color) => <span class={`badge badge-outline text-${color}`}>{ucFirst(color)}</span>)}
</BadgeList>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>With icons</CardTitle>
<div class="card-subtitle">Add an icon before the label to reinforce its meaning.</div>
<BadgeList>
{
colors.map((color) => (
<span class={`badge bg-${color} text-${color}-fg`}>
{' '}
<Icon name="star" type="filled" /> {ucFirst(color)}{' '}
</span>
))
}
</BadgeList>
</CardBody>
</Card>
</div>
</div>
</DefaultLayout>
+12 -1
View File
@@ -1,9 +1,20 @@
---
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import CarouselCard from '@shared/components/cards/CarouselCard.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const carouselDocsUrl = getDocsUrl('/ui/components/carousel')
---
<DefaultLayout title="Carousel" pageHeader="Carousel" pageMenu="base.carousel">
<DefaultLayout title="Carousel" pageMenu="base.carousel" description="A carousel cycles through a set of slides, with optional indicators and controls.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Carousel</h1>
<a href={carouselDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-md-6">
<CarouselCard id="default" />
+29 -7
View File
@@ -6,7 +6,9 @@ import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import Avatar from '@ui/Avatar.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import site from '@data/site.json'
import { getDocsUrl } from '@shared/lib/string-format'
type ColorEntry = [string, { title: string; hex: string; abbr?: string; icon?: string }]
@@ -16,13 +18,24 @@ const grayColors = Object.entries(site.grayColors) as ColorEntry[]
const socialColors = Object.entries(site.socialColors) as ColorEntry[]
const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'transparent']
const colorsDocsUrl = getDocsUrl('/ui/base/colors')
---
<DefaultLayout title="Colors" pageHeader="Colors" pageMenu="base.colors">
<DefaultLayout title="Colors" pageMenu="base.colors" description="The full color palette, with hex values, and a gradient builder.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Colors</h1>
<a href={colorsDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-3">
<div class="col-6 col-lg-3">
<Card>
<CardBody>
<CardTitle>Colors</CardTitle>
<div class="card-subtitle">The extended color palette, with hex values.</div>
<div class="row g-3">
{
colors.map(([name, color]) => (
@@ -44,9 +57,11 @@ const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'trans
</CardBody>
</Card>
</div>
<div class="col-3">
<div class="col-6 col-lg-3">
<Card>
<CardBody>
<CardTitle>Light colors</CardTitle>
<div class="card-subtitle">A tinted, low-contrast version of each color.</div>
<div class="row g-3">
{
lightColors.map(([name, color]) => (
@@ -68,9 +83,11 @@ const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'trans
</CardBody>
</Card>
</div>
<div class="col-3">
<div class="col-6 col-lg-3">
<Card>
<CardBody>
<CardTitle>Gray colors</CardTitle>
<div class="card-subtitle">The neutral gray scale used for text, borders, and backgrounds.</div>
<div class="row g-3">
{
grayColors.map(([name, color]) => (
@@ -92,9 +109,11 @@ const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'trans
</CardBody>
</Card>
</div>
<div class="col-3">
<div class="col-6 col-lg-3">
<Card>
<CardBody>
<CardTitle>Social colors</CardTitle>
<div class="card-subtitle">Brand colors for social networks.</div>
<div class="row g-3">
{
socialColors.map(([name, color]) => (
@@ -118,10 +137,11 @@ const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'trans
</div>
<div class="col-12">
<div class="row">
<div class="col-6">
<div class="col-12 col-lg-6">
<Card>
<CardBody>
<CardTitle>Gradient</CardTitle>
<div class="card-subtitle">Build a gradient from any two colors and preview it live.</div>
<form action="">
<div class="row g-4">
<div class="col">
@@ -174,9 +194,11 @@ const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'trans
</CardBody>
</Card>
</div>
<div class="col-6">
<div class="col-12 col-lg-6">
<Card>
<CardBody>
<CardTitle>Gradient colors</CardTitle>
<div class="card-subtitle">Every color faded to transparent, fading in from the left or the right.</div>
<div class="row">
<div class="col">
<div class="space-y">
+12 -1
View File
@@ -3,10 +3,21 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Card from '@ui/Card.astro'
import CardHeader from '@ui/CardHeader.astro'
import CardBody from '@ui/CardBody.astro'
import Icon from '@ui/Icon.astro'
import Datagrid from '@shared/components/parts/Datagrid.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const datagridDocsUrl = getDocsUrl('/ui/components/datagrid')
---
<DefaultLayout title="Data grid" pageHeader="Data grid" pageMenu="base.datagrid">
<DefaultLayout title="Data grid" pageMenu="base.datagrid" description="A data grid lays out label/value pairs in a compact, aligned grid.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Data grid</h1>
<a href={datagridDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<Card>
<CardHeader title="Base info" />
<CardBody>
+12 -1
View File
@@ -2,9 +2,20 @@
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import DropdownMenu from '@ui/DropdownMenu.astro'
import DropdownMenuAll from '@ui/DropdownMenuAll.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const dropdownDocsUrl = getDocsUrl('/ui/components/dropdown')
---
<DefaultLayout title="Dropdowns" pageHeader="Dropdowns" pageMenu="base.dropdowns">
<DefaultLayout title="Dropdowns" pageMenu="base.dropdowns" description="Dropdown menus for actions, filters, and selections, always shown open here for reference.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Dropdowns</h1>
<a href={dropdownDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row">
<div class="col-sm-6 col-lg-3">
<DropdownMenuAll show />
+12 -1
View File
@@ -2,14 +2,25 @@
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Card from '@ui/Card.astro'
import CardHeader from '@ui/CardHeader.astro'
import Icon from '@ui/Icon.astro'
import UsersList from '@shared/components/cards/UsersList.astro'
import UsersList2 from '@shared/components/cards/UsersList2.astro'
import UsersListHeaders from '@shared/components/cards/UsersListHeaders.astro'
import ListGroup from '@ui/ListGroup.astro'
import ListGroupItem from '@ui/ListGroupItem.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const listGroupDocsUrl = getDocsUrl('/ui/components/list-group')
---
<DefaultLayout title="Lists" pageHeader="Lists" pageMenu="base.lists">
<DefaultLayout title="Lists" pageMenu="base.lists" description="Lists of users, contacts, and links — built from list-group items or a plain card body.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Lists</h1>
<a href={listGroupDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-md-6">
<div class="row row-cards">
+12 -1
View File
@@ -4,6 +4,7 @@
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import Icon from '@ui/Icon.astro'
import ModalInline from '@shared/components/modals/ModalInline.astro'
import SimpleModalContent from '@shared/components/modals/SimpleModalContent.astro'
import LargeModalContent from '@shared/components/modals/LargeModalContent.astro'
@@ -22,11 +23,21 @@ import EditProfileModalContent from '@shared/components/modals/EditProfileModalC
import ConfirmDeleteModalContent from '@shared/components/modals/ConfirmDeleteModalContent.astro'
import ChangePasswordModalContent from '@shared/components/modals/ChangePasswordModalContent.astro'
import AddTaskModalContent from '@shared/components/modals/AddTaskModalContent.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const cardClass = 'position-relative rounded d-block bg-surface-backdrop py-6 w-auto h-auto z-0'
const modalDocsUrl = getDocsUrl('/ui/components/modal')
---
<DefaultLayout title="Modals" pageHeader="Modals" pageMenu="base.modals" pageLibs={['signature_pad', 'hugerte', 'litepicker', 'tom-select']}>
<DefaultLayout title="Modals" pageMenu="base.modals" pageLibs={['signature_pad', 'hugerte', 'litepicker', 'tom-select']} description="Modals interrupt the page to ask for input, confirmation, or a focused action.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Modals</h1>
<a href={modalDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<Card>
<CardBody>
<div class="row g-5">
+12 -1
View File
@@ -4,9 +4,20 @@
// fluid-search (variant 5) is a no-op — search block in condensed branch is dead code.
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Navbar from '@shared/components/navbar/Navbar.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const navbarDocsUrl = getDocsUrl('/ui/layout/navbars')
---
<DefaultLayout title="Navigation" pageHeader="Navigation" pageMenu="base.navigation">
<DefaultLayout title="Navigation" pageMenu="base.navigation" description="Navbar variants — condensed, dark, colored, and with different logo and search layouts.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Navigation</h1>
<a href={navbarDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="box">
<div class="mb-3">
<Navbar sample condensed transparent personId={3} />
+12 -2
View File
@@ -1,17 +1,27 @@
---
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import { ucFirst as capitalize } from '@shared/lib/string-format'
import { ucFirst as capitalize, getDocsUrl } from '@shared/lib/string-format'
import ButtonList from '@ui/ButtonList.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import Icon from '@ui/Icon.astro'
import Offcanvas from '@ui/Offcanvas.astro'
import OffcanvasHeader from '@ui/OffcanvasHeader.astro'
import OffcanvasBody from '@ui/OffcanvasBody.astro'
const directions = ['start', 'end', 'top', 'bottom'] as const
const offcanvasDocsUrl = getDocsUrl('/ui/components/offcanvas')
---
<DefaultLayout title="Offcanvas" pageHeader="Offcanvas" pageMenu="base.offcanvas">
<DefaultLayout title="Offcanvas" pageMenu="base.offcanvas" description="An offcanvas panel slides in from an edge of the screen for navigation or supplementary content.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Offcanvas</h1>
<a href={offcanvasDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<Card>
<CardBody>
<ButtonList>
+17 -1
View File
@@ -3,13 +3,27 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Pagination from '@ui/Pagination.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const paginationDocsUrl = getDocsUrl('/ui/components/pagination')
---
<DefaultLayout title="Pagination" pageHeader="Pagination" pageMenu="base.pagination">
<DefaultLayout title="Pagination" pageMenu="base.pagination" description="Pagination splits a long list of results into pages, with Prev/Next controls.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Pagination</h1>
<a href={paginationDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Numbered</CardTitle>
<div class="card-subtitle">Page numbers, with or without <code>text</code> Prev/Next labels.</div>
<Pagination />
<Pagination text />
</CardBody>
@@ -18,6 +32,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-12">
<Card>
<CardBody>
<CardTitle>With descriptions</CardTitle>
<div class="card-subtitle">Add <code>prevDescription</code>/<code>nextDescription</code> to name the adjacent page.</div>
<Pagination count={0} prevDescription="Getting started" nextDescription="Breadcrumbs" />
</CardBody>
</Card>
+12 -1
View File
@@ -3,16 +3,27 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import BackgroundPattern from '@ui/BackgroundPattern.astro'
import site from '@data/site.json'
import { getDocsUrl } from '@shared/lib/string-format'
const patternColors = Object.keys(site.colors)
const patternSizes = ['sm', 'md', 'lg', 'xl'] as const
const patterns = ['diagonal', 'diagonal-2', 'dots', 'rectangles', 'lines', 'lines-vertical', 'grid', 'grid-diagonal', 'blueprint', 'circles', 'diagonal-stripes', 'diagonal-stripes-2', 'zigzag', 'vertical-stripes', 'horizontal-stripes'] as const
const patternsDocsUrl = getDocsUrl('/ui/utilities/background-patterns')
---
<DefaultLayout title="Patterns" pageHeader="Patterns" pageMenu="base.patterns">
<DefaultLayout title="Patterns" pageMenu="base.patterns" description="Background patterns you can apply to any element with a utility class.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Patterns</h1>
<a href={patternsDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-12">
<Card>
+16 -5
View File
@@ -1,25 +1,36 @@
---
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Icon from '@ui/Icon.astro'
import PlaceholderCard1 from '@shared/components/cards/placeholder/Card1.astro'
import PlaceholderCard2 from '@shared/components/cards/placeholder/Card2.astro'
import PlaceholderCard3 from '@shared/components/cards/placeholder/Card3.astro'
import PlaceholderCard4 from '@shared/components/cards/placeholder/Card4.astro'
import PlaceholderCard5 from '@shared/components/cards/placeholder/Card5.astro'
import PlaceholderCard6 from '@shared/components/cards/placeholder/Card6.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const cols = [1, 2, 3, 4]
const placeholderDocsUrl = getDocsUrl('/ui/components/placeholder')
---
<DefaultLayout title="Placeholder" pageHeader="Placeholder" pageMenu="base.placeholder">
<DefaultLayout title="Placeholder" pageMenu="base.placeholder" description="Placeholders mimic the shape of content that hasn't loaded yet.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Placeholder</h1>
<a href={placeholderDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
{
cols.map(() => (
<div class="col-3">
<div class="col-6 col-md-3">
<PlaceholderCard1 />
</div>
))
}
<div class="col-4">
<div class="col-12 col-md-4">
<div class="row row-cards">
<div class="col-12">
<PlaceholderCard3 />
@@ -29,7 +40,7 @@ const cols = [1, 2, 3, 4]
</div>
</div>
</div>
<div class="col-4">
<div class="col-12 col-md-4">
<div class="row row-cards">
<div class="col-12">
<PlaceholderCard1 />
@@ -39,7 +50,7 @@ const cols = [1, 2, 3, 4]
</div>
</div>
</div>
<div class="col-4">
<div class="col-12 col-md-4">
<div class="row row-cards">
<div class="col-12">
<PlaceholderCard5 />
+155
View File
@@ -0,0 +1,155 @@
---
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Prose from '@ui/Prose.astro'
import Icon from '@ui/Icon.astro'
import Avatar from '@ui/Avatar.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const proseDocsUrl = getDocsUrl('/ui/base/prose')
---
<DefaultLayout title="Prose" pageMenu="base.prose" description="Wrap long-form content in .prose to style headings, paragraphs, lists, and tables without adding classes to every element.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Prose</h1>
<a href={proseDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row justify-content-center">
<div class="col-lg-9 col-xl-8">
<div class="card card-lg">
<div class="card-body">
<Prose>
<h1>Getting started with documentation <span class="badge">New</span></h1>
<div class="d-flex align-items-center text-secondary mb-4">
<Avatar placeholder="PK" size="sm" class="me-2" />
Written by Paweł Kuna · 6 min read
</div>
<p class="lead">
Writing clear and effective documentation is essential for any project. When you create content that others will read and use, proper formatting makes all the difference. <em>Good documentation</em> helps users understand complex concepts quickly and efficiently.
</p>
<blockquote>
<p>Documentation is a love letter that you write to your future self.</p>
</blockquote>
<p>
The foundation of great documentation starts with <strong>understanding your audience</strong> and their needs. Along the way you'll reach for a handful of inline elements: <mark>highlights</mark> to draw the eye, an <abbr title="HyperText Markup Language">HTML</abbr> tag for terms that deserve a definition,
and <del>outdated advice</del> replaced with <ins>the current recommendation</ins>. Formulas read naturally too, like H<sub>2</sub>O or 2<sup>10</sup>.
</p>
<p>
As <cite>The Elements of Style</cite> puts it, omit needless words — the same rule applies to interfaces and to the docs that explain them.
</p>
<div class="callout">
<p>
<strong>Tip.</strong> Keep a glossary of terms your readers might not know. It saves you from re-explaining the same concept in every article.
</p>
</div>
<h2>A quick glossary</h2>
<dl>
<dt>Prose</dt>
<dd>Long-form, readable content — articles, changelogs, help center entries.</dd>
<dt>Markdown</dt>
<dd>A plain-text format that compiles down to the same HTML this page uses.</dd>
<dt>Style guide</dt>
<dd>A shared reference so every writer on the team formats things the same way.</dd>
</dl>
<h2>Adding code examples</h2>
<p>
Inline code looks like <code>console.log("Hello")</code> and uses monospace styling. For anything longer, drop it into a code block, and mention shortcuts with <kbd>Ctrl</kbd> + <kbd>C</kbd> style keys:
</p>
<pre><code>{`function add(a, b) {\n return a + b;\n}`}</code></pre>
<h2>Structuring steps and points</h2>
<p>Use an ordered list to walk through a process:</p>
<ol>
<li>Start with the most important information.</li>
<li>Provide context before technical details.</li>
<li>Include practical examples.</li>
</ol>
<p>And an unordered list for related points that don't have a specific order:</p>
<ul>
<li>Write clear, concise list items.</li>
<li>Keep the structure consistent.</li>
<li>Avoid overly long items.</li>
</ul>
<h3>Nesting related items</h3>
<p>Lists can nest, with indentation and spacing handled automatically:</p>
<ul>
<li>
Editing
<ul>
<li>Undo and redo</li>
<li>Cut, copy, and paste</li>
</ul>
</li>
<li>
Formatting
<ul>
<li>Bold and italic</li>
<li>Headings and lists</li>
</ul>
</li>
<li>Sharing</li>
</ul>
<h3>A visual checklist</h3>
<p>For a status readers can scan at a glance, drop in the steps component instead of a plain list:</p>
<ul class="steps steps-vertical steps-counter my-4">
<li class="step-item">Draft</li>
<li class="step-item">Review</li>
<li class="step-item active">Publish</li>
<li class="step-item">Announce</li>
</ul>
<hr />
<h2>Supporting images</h2>
<p>Images should support the text and provide useful context, not just decorate the page.</p>
<figure>
<img src="/static/photos/cup-of-coffee-and-an-open-book.jpg" alt="Open book and coffee" />
<figcaption class="text-secondary small mt-2">A quiet spot makes for better writing sessions.</figcaption>
</figure>
<h2>Comparing data in tables</h2>
<p>Tables work best for comparing related data points, like votes on a proposal:</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Up-votes</th>
<th>Down-votes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice</td>
<td>10</td>
<td>11</td>
</tr>
<tr>
<td>Bob</td>
<td>4</td>
<td>3</td>
</tr>
<tr>
<td>Charlie</td>
<td>7</td>
<td>9</td>
</tr>
<tr>
<td>Totals</td>
<td>21</td>
<td>23</td>
</tr>
</tbody>
</table>
</Prose>
</div>
</div>
</div>
</div>
</DefaultLayout>
+37 -1
View File
@@ -3,13 +3,27 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import NavSegmented from '@ui/NavSegmented.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const segmentedControlDocsUrl = getDocsUrl('/ui/components/segmented-control')
---
<DefaultLayout title="Segmented control" pageHeader="Segmented control" pageMenu="base.segmented-control">
<DefaultLayout title="Segmented control" pageMenu="base.segmented-control" description="A segmented control lets users pick one option from a small, always-visible set.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Segmented control</h1>
<a href={segmentedControlDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Basic</CardTitle>
<div class="card-subtitle">A list of <code>items</code> is enough for a plain control.</div>
<NavSegmented items={['1', '2', '3', '4']} />
</CardBody>
</Card>
@@ -17,6 +31,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Any content</CardTitle>
<div class="card-subtitle">Items can be any short text, including emoji.</div>
<NavSegmented items={['👦', '👦🏿', '👦🏾', '👦🏽', '👦🏼', '👦🏻']} />
</CardBody>
</Card>
@@ -24,6 +40,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Icons only</CardTitle>
<div class="card-subtitle">Pass <code>icons</code> without <code>items</code> for an icon-only control.</div>
<NavSegmented icons={['home', 'star', 'clock', 'ghost', 'bold', 'italic', 'underline']} />
</CardBody>
</Card>
@@ -31,6 +49,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>With icons</CardTitle>
<div class="card-subtitle">Combine <code>items</code> and <code>icons</code> to label each option.</div>
<NavSegmented items={['List', 'Kanban', 'Calendar', 'Files']} icons={['list', 'layout', 'calendar', 'files']} />
</CardBody>
</Card>
@@ -38,6 +58,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Vertical</CardTitle>
<div class="card-subtitle">Add <code>vertical</code> to stack the options in a column.</div>
<NavSegmented items={['List', 'Kanban', 'Calendar', 'Files']} icons={['list', 'layout', 'calendar', 'files']} vertical />
</CardBody>
</Card>
@@ -45,6 +67,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Vertical with disabled items</CardTitle>
<div class="card-subtitle">Pass indexes to <code>disabled</code> to make specific options unselectable.</div>
<NavSegmented icons={['home', 'star', 'clock', 'ghost', 'bold', 'italic', 'underline']} vertical disabled={[3]} />
</CardBody>
</Card>
@@ -52,6 +76,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Disabled items</CardTitle>
<div class="card-subtitle">Disabled options stay visible but can't be selected.</div>
<NavSegmented items={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} disabled={[4, 5]} />
</CardBody>
</Card>
@@ -59,6 +85,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Vertical, no icons</CardTitle>
<div class="card-subtitle">A vertical control also works with text-only items.</div>
<NavSegmented items={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} vertical={true} />
</CardBody>
</Card>
@@ -66,6 +94,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Independent group</CardTitle>
<div class="card-subtitle">Set a unique <code>name</code> so this control doesn't share selection state with others using the same items.</div>
<NavSegmented items={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} name="checkbox" />
</CardBody>
</Card>
@@ -73,6 +103,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Full width</CardTitle>
<div class="card-subtitle">Add <code>fullWidth</code> to stretch the control across its container.</div>
<NavSegmented items={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} fullWidth={true} />
</CardBody>
</Card>
@@ -80,6 +112,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Full width, stacked</CardTitle>
<div class="card-subtitle">Multiple full-width controls, one above the other.</div>
<div class="space-y">
<div><NavSegmented items={['Overview', 'Analytics', 'Reports', 'Notifications']} fullWidth={true} /></div>
<div><NavSegmented items={['Account', 'Password']} fullWidth={true} /></div>
@@ -90,6 +124,8 @@ import CardBody from '@ui/CardBody.astro'
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Sizes</CardTitle>
<div class="card-subtitle">Set <code>size</code> to <code>sm</code> or <code>lg</code> — with text, icons, or both.</div>
<div class="space-y">
<div><NavSegmented items={['List', 'Kanban', 'Calendar', 'Files']} disabled={[3, 5]} size="sm" /></div>
<div><NavSegmented items={['List', 'Kanban', 'Calendar', 'Files']} disabled={[3, 5]} /></div>
+20 -9
View File
@@ -4,10 +4,12 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import ButtonList from '@ui/ButtonList.astro'
import Trending from '@ui/Trending.astro'
import Card from '@ui/Card.astro'
import CardHeader from '@ui/CardHeader.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import socialTiles from '@data/social-tiles.json'
import socials from '@data/socials.json'
import { getDocsUrl } from '@shared/lib/string-format'
interface SocialTile {
icon: string
@@ -24,15 +26,24 @@ interface Social {
const tiles = socialTiles as SocialTile[]
const socialList = socials as Social[]
const fillers = Array.from({ length: 21 })
const socialDocsUrl = getDocsUrl('/ui/plugins/social-icons')
---
<DefaultLayout title="Social icons" pageHeader="Social icons" pageMenu="base.social">
<DefaultLayout title="Social icons" pageMenu="base.social" description="Branded icons and helpers for linking out to, or signing in with, social networks.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Social icons</h1>
<a href={socialDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-12">
<div class="row g-3">
{
tiles.map((tile) => (
<div class="col-3">
<div class="col-6 col-md-4 col-lg-3">
<Card>
<CardBody>
<div class="row align-items-center">
@@ -56,10 +67,9 @@ const fillers = Array.from({ length: 21 })
</div>
<div class="col-12">
<Card>
<CardHeader>
<h3 class="card-title">Sign in with social media</h3>
</CardHeader>
<CardBody>
<CardTitle>Sign in with social media</CardTitle>
<div class="card-subtitle">A button per provider, for a social sign-in screen.</div>
<ButtonList>
{
socialList.map((social) => (
@@ -76,9 +86,10 @@ const fillers = Array.from({ length: 21 })
<div class="col-12">
<Card>
<CardHeader>
<h3 class="card-title">List of all social media icons</h3>
</CardHeader>
<CardBody>
<CardTitle>List of all social media icons</CardTitle>
<div class="card-subtitle">Every brand icon shipped with the social icons plugin.</div>
</CardBody>
<CardBody class="p-0">
<div class="demo-icons-list-wrap">
<div class="demo-icons-list">
+63 -12
View File
@@ -3,35 +3,65 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const stepDocsUrl = getDocsUrl('/ui/components/step')
---
<DefaultLayout title="Steps" pageHeader="Steps" pageMenu="base.steps">
<div class="row row-cards">
<div class="col-lg-8">
<DefaultLayout title="Steps" pageMenu="base.steps" description="Steps show progress through a numbered or labeled sequence, as a horizontal bar, breadcrumb, or vertical timeline.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Steps</h1>
<a href={stepDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards mb-5">
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Steps horizontal</CardTitle>
<CardTitle>Horizontal</CardTitle>
<div class="card-subtitle">Numbered steps for a linear process, with the current step highlighted.</div>
<ul class="steps steps-green my-4">
<li class="step-item">1</li>
<li class="step-item active">2</li>
<li class="step-item">3</li>
</ul>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Horizontal counter</CardTitle>
<div class="card-subtitle">The same steps without numbers, for a more minimal look.</div>
<ul class="steps steps-green steps-counter my-4">
<li class="step-item"></li>
<li class="step-item active"></li>
<li class="step-item"></li>
</ul>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>With labels</CardTitle>
<div class="card-subtitle">Give each step a title instead of a number.</div>
<ul class="steps steps-green steps-counter my-4">
<li class="step-item">Cart</li>
<li class="step-item active">Billing Information</li>
<li class="step-item">Confirmation</li>
</ul>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Breadcrumb arrows</CardTitle>
<div class="card-subtitle">Chevron-style steps, useful for wizards and checkouts.</div>
<ol class="breadcrumb breadcrumb-arrows">
<li class="breadcrumb-item"><a href="#">Step one</a></li>
<li class="breadcrumb-item active"><a href="#">Step two</a></li>
@@ -39,7 +69,13 @@ import CardTitle from '@ui/CardTitle.astro'
<li class="breadcrumb-item disabled"><a href="#">Step four</a></li>
</ol>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Breadcrumb numbered</CardTitle>
<div class="card-subtitle">Number each step and mute the ones not yet reached.</div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">1. Step one</a></li>
<li class="breadcrumb-item"><a href="#">2. Step two</a></li>
@@ -48,7 +84,13 @@ import CardTitle from '@ui/CardTitle.astro'
<li class="breadcrumb-item disabled"><a href="#">5. Step five</a></li>
</ol>
</CardBody>
</Card>
</div>
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Breadcrumb muted</CardTitle>
<div class="card-subtitle">A quieter style for steps that shouldn't compete with the rest of the page.</div>
<ol class="breadcrumb breadcrumb-muted">
<li class="breadcrumb-item"><a href="#">1. Step one</a></li>
<li class="breadcrumb-item"><a href="#">2. Step two</a></li>
@@ -59,31 +101,40 @@ import CardTitle from '@ui/CardTitle.astro'
</CardBody>
</Card>
</div>
</div>
<div class="col-lg-4">
<div class="row row-cards">
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Steps vertical</CardTitle>
<CardTitle>Vertical</CardTitle>
<div class="card-subtitle">A title and description per step, for a detailed timeline.</div>
<ul class="steps steps-vertical">
<li class="step-item">
<div class="h4 m-0">Order received</div>
<div class="text-secondary">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus culpa cum expedita ipsam laborum nam ratione reprehenderit sed sint tenetur!</div>
<div class="text-secondary">The order was placed and payment was confirmed. A confirmation email is on its way.</div>
</li>
<li class="step-item">
<div class="h4 m-0">Order received</div>
<div class="text-secondary">Lorem ipsum dolor sit amet.</div>
<div class="h4 m-0">Preparing</div>
<div class="text-secondary">The warehouse picked and packed the items.</div>
</li>
<li class="step-item active">
<div class="h4 m-0">Out for delivery</div>
<div class="text-secondary">Lorem ipsum dolor sit amet.</div>
<div class="text-secondary">The courier has the package and is on the way.</div>
</li>
<li class="step-item">
<div class="h4 m-0">Finalized</div>
<div class="text-secondary">Lorem ipsum dolor sit amet.</div>
<div class="h4 m-0">Delivered</div>
<div class="text-secondary">Signed for at the delivery address.</div>
</li>
</ul>
</CardBody>
</Card>
</div>
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Vertical counter</CardTitle>
<div class="card-subtitle">Numbered steps in a vertical list, without descriptions.</div>
<ul class="steps steps-counter steps-vertical">
<li class="step-item">Step one</li>
<li class="step-item">Step two</li>
+12 -1
View File
@@ -1,9 +1,20 @@
---
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import TabsCard from '@shared/components/cards/TabsCard.astro'
import Icon from '@ui/Icon.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const tabsDocsUrl = getDocsUrl('/ui/layout/navs-tabs')
---
<DefaultLayout title="Tabs" pageHeader="Tabs" pageMenu="base.tabs">
<DefaultLayout title="Tabs" pageMenu="base.tabs" description="Tabs switch between related panels of content inside a card.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Tabs</h1>
<a href={tabsDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-md-4"><TabsCard id="1" settings /></div>
<div class="col-md-4"><TabsCard id="2" reverse settings /></div>
+22 -3
View File
@@ -5,9 +5,11 @@ import TagList from '@ui/TagList.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import people from '@data/people.json'
import flags from '@data/flags.json'
import siteData from '@data/site.json'
import { getDocsUrl } from '@shared/lib/string-format'
const tagIcons = ['bold', 'italic', 'underline', 'copy', 'scissors', 'file-plus', 'file-minus', 'ghost', 'star', 'script', 'photo', 'dog', 'piano']
@@ -18,14 +20,24 @@ const people8 = people.slice(0, 8)
// site.colors yields value objects with .class / .title.
const colors = Object.values(siteData.colors) as { class: string; title: string }[]
const tagDocsUrl = getDocsUrl('/ui/components/tag')
---
<DefaultLayout title="Tags" pageHeader="Tags" pageMenu="base.tags">
<DefaultLayout title="Tags" pageMenu="base.tags" description="Tags are compact labels for filters, categories, and keywords.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Tags</h1>
<a href={tagDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards row-cols-1 row-cols-md-2 row-cols-lg-3">
<div class="col">
<Card>
<CardBody>
<CardTitle>Default tags</CardTitle>
<div class="card-subtitle">The base tag, for a plain label.</div>
<TagList>
{range(1, 14).map((i) => <Tag text={`Label ${i}`} />)}
</TagList>
@@ -37,6 +49,7 @@ const colors = Object.values(siteData.colors) as { class: string; title: string
<Card>
<CardBody>
<CardTitle>Tags with flag</CardTitle>
<div class="card-subtitle">Pair a tag with a country flag.</div>
<TagList>
{flags9.map((country) => <Tag text={country.name} flag={country.flag} />)}
</TagList>
@@ -48,6 +61,7 @@ const colors = Object.values(siteData.colors) as { class: string; title: string
<Card>
<CardBody>
<CardTitle>Tags with icon</CardTitle>
<div class="card-subtitle">Add an icon before the label.</div>
<TagList>
{tagIcons.map((icon) => <Tag text={icon} icon={icon} />)}
</TagList>
@@ -59,6 +73,7 @@ const colors = Object.values(siteData.colors) as { class: string; title: string
<Card>
<CardBody>
<CardTitle>Tags with avatar</CardTitle>
<div class="card-subtitle">Add a person's avatar before the label.</div>
<TagList>
{people8.map((person) => <Tag text={person.full_name} person={person} />)}
</TagList>
@@ -70,6 +85,7 @@ const colors = Object.values(siteData.colors) as { class: string; title: string
<Card>
<CardBody>
<CardTitle>Tags with status</CardTitle>
<div class="card-subtitle">Add a small status dot in any theme or extended color.</div>
<TagList>
{colors.map((color) => <Tag text={color.title} status={color.class} />)}
</TagList>
@@ -81,6 +97,7 @@ const colors = Object.values(siteData.colors) as { class: string; title: string
<Card>
<CardBody>
<CardTitle>Tags with legend</CardTitle>
<div class="card-subtitle">Add a colored legend square instead of a dot.</div>
<TagList>
{colors.map((color) => <Tag text={color.title} legend={color.class} />)}
</TagList>
@@ -91,7 +108,8 @@ const colors = Object.values(siteData.colors) as { class: string; title: string
<div class="col">
<Card>
<CardBody>
<CardTitle>Default tags</CardTitle>
<CardTitle>Selectable tags</CardTitle>
<div class="card-subtitle">Add <code>checkbox</code> to let users toggle a tag on and off.</div>
<TagList>
{range(1, 6).map((i) => <Tag text={`Label ${i}`} checkbox={true} />)}
{range(7, 12).map((i) => <Tag text={`Label ${i}`} checkbox={true} checked={true} />)}
@@ -103,7 +121,8 @@ const colors = Object.values(siteData.colors) as { class: string; title: string
<div class="col">
<Card>
<CardBody>
<CardTitle>Default tags</CardTitle>
<CardTitle>Tags with badge</CardTitle>
<div class="card-subtitle">Add a <code>badge</code> count to the end of the tag.</div>
<TagList>
{range(1, 12).map((i) => <Tag text="Label" badge={i} />)}
</TagList>
+12 -1
View File
@@ -4,10 +4,21 @@ import Button from '@ui/Button.astro'
import ButtonList from '@ui/ButtonList.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import Icon from '@ui/Icon.astro'
import Toast from '@ui/Toast.astro'
import { getDocsUrl } from '@shared/lib/string-format'
const toastDocsUrl = getDocsUrl('/ui/components/toast')
---
<DefaultLayout title="Toasts" pageHeader="Toasts" pageMenu="base.toasts">
<DefaultLayout title="Toasts" pageMenu="base.toasts" description="Toasts show a brief, dismissible notification in the corner of the screen.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Toasts</h1>
<a href={toastDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<Card>
<CardBody>
<ButtonList>
+15 -2
View File
@@ -3,7 +3,9 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardTitle from '@ui/CardTitle.astro'
import Icon from '@ui/Icon.astro'
import StatusMonitoring from '@shared/components/cards/StatusMonitoring.astro'
import { getDocsUrl } from '@shared/lib/string-format'
interface TrackingItem {
status?: 'success' | 'warning' | 'danger'
@@ -43,14 +45,24 @@ const uptime: TrackingItem[] = [
{ status: 'success', label: 'Operational' },
{ status: 'success', label: 'Operational' },
]
const trackingDocsUrl = getDocsUrl('/ui/components/tracking')
---
<DefaultLayout title="Tracking" pageHeader="Tracking" pageMenu="base.tracking">
<DefaultLayout title="Tracking" pageMenu="base.tracking" description="Tracking blocks show an uptime or activity history as a row of colored bars.">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Tracking</h1>
<a href={trackingDocsUrl} target="_blank" rel="noopener" class="link-secondary">
Docs <Icon name="external-link" class="icon-sm" />
</a>
</div>
<div class="row row-cards">
<div class="col-12">
<Card>
<CardBody>
<CardTitle>Basic example</CardTitle>
<div class="card-subtitle">Hover a block to see its status.</div>
<div class="tracking">
{uptime.map((item) => <div class:list={['tracking-block', item.status && `bg-${item.status}`]} data-bs-toggle="tooltip" data-bs-placement="top" title={item.label} />)}
</div>
@@ -65,7 +77,8 @@ const uptime: TrackingItem[] = [
<div class="col-md-6">
<Card>
<CardBody>
<CardTitle>Squares variant (.tracking-squares)</CardTitle>
<CardTitle>Squares variant</CardTitle>
<div class="card-subtitle">Add <code>.tracking-squares</code> for square, evenly spaced blocks.</div>
<div class="tracking tracking-squares">
{uptime.map((item) => <div class:list={['tracking-block', item.status && `bg-${item.status}`]} data-bs-toggle="tooltip" data-bs-placement="top" title={item.label} />)}
</div>