diff --git a/.changeset/icon-list.md b/.changeset/icon-list.md
new file mode 100644
index 000000000..a3fa72a11
--- /dev/null
+++ b/.changeset/icon-list.md
@@ -0,0 +1,7 @@
+---
+"@tabler/core": minor
+"@tabler/preview": minor
+"@tabler/docs": minor
+---
+
+Added the icon list component: `.list-icon` turns a plain list into a list with icon markers, such as a checklist of features, with `.list-icon-inline` for entries in a row.
diff --git a/core/scss/_variables.scss b/core/scss/_variables.scss
index dc44b7357..47a9a2205 100644
--- a/core/scss/_variables.scss
+++ b/core/scss/_variables.scss
@@ -2043,6 +2043,15 @@ $list-group-item-gap-lg: 1.25rem !default;
$list-group-variant-border-alpha: 0.24 !default;
$list-group-variant-hover-alpha: 0.16 !default;
+// Icon list
+// scss-docs-start list-icon-variables
+$list-icon-gap: 0.5rem !default;
+$list-icon-row-gap: 0.5rem !default;
+$list-icon-inline-gap: 1.5rem !default;
+$list-icon-size: 1rem !default;
+$list-icon-color: var(--secondary) !default;
+// scss-docs-end list-icon-variables
+
// Forms
$input-disabled-bg: var(--bg-forms-disabled) !default;
$input-border-color: var(--border-color) !default;
diff --git a/core/scss/ui/_lists.scss b/core/scss/ui/_lists.scss
index e7ec6f6ba..351e563e5 100644
--- a/core/scss/ui/_lists.scss
+++ b/core/scss/ui/_lists.scss
@@ -204,3 +204,42 @@
content: ' · ';
}
}
+
+// Icon list: a list whose markers are icons instead of bullets, such as a
+// checklist of features. The first `.icon` in an entry is the marker.
+.list-icon {
+ // scss-docs-start list-icon-css-vars
+ --list-icon-gap: #{$list-icon-gap};
+ --list-icon-row-gap: #{$list-icon-row-gap};
+ --list-icon-inline-gap: #{$list-icon-inline-gap};
+ --list-icon-size: #{$list-icon-size};
+ --list-icon-color: #{$list-icon-color};
+ // scss-docs-end list-icon-css-vars
+ display: flex;
+ flex-direction: column;
+ gap: var(--list-icon-row-gap);
+ padding-inline-start: 0;
+ list-style: none;
+
+ > * {
+ display: flex;
+ gap: var(--list-icon-gap);
+ align-items: flex-start;
+ }
+
+ > * > .icon:first-child {
+ --icon-size: var(--list-icon-size);
+ flex-shrink: 0;
+ // center the marker on the first line of text: `.icon` sets its own font
+ // size, which would make `1lh` the icon's line height, so inherit the text's
+ margin-top: calc((1lh - var(--list-icon-size)) / 2);
+ font-size: inherit;
+ color: var(--list-icon-color);
+ }
+}
+
+// Entries in a row that wraps, for a short list of highlights
+.list-icon-inline {
+ flex-flow: row wrap;
+ gap: var(--list-icon-row-gap) var(--list-icon-inline-gap);
+}
diff --git a/docs/content/ui/components/icon-list.mdx b/docs/content/ui/components/icon-list.mdx
new file mode 100644
index 000000000..dc485ad03
--- /dev/null
+++ b/docs/content/ui/components/icon-list.mdx
@@ -0,0 +1,174 @@
+---
+title: Icon list
+summary: An icon list replaces the bullets of a list with icons, such as a check mark in a list of features, a plan's benefits or the steps of a setup.
+description: Show a list with icon markers instead of bullets. Change the icon and the color per list or per entry, or lay the entries out in a row.
+related: [/ui/components/list-group, /ui/components/icon]
+source: core/scss/ui/_lists.scss
+css-vars: list-icon
+classnames:
+ component:
+ - class: list-icon
+ desc: The list, on a ul, ol or div
+ part:
+ - class: icon
+ desc: The first icon in an entry becomes its marker
+ direction:
+ - class: list-icon-inline
+ desc: Lays the entries out in a row that wraps
+ color:
+ - class: text-{color}
+ desc: Colors the marker of one entry, for example text-green on the icon
+---
+import Example from '@components/Example.astro';
+import Icon from '@ui/Icon.astro';
+
+## Default markup
+
+Add `.list-icon` to a list and start each entry with an icon. The first icon in an entry is the marker: it sits in front of the text, centered on the first line, in the secondary color.
+
+
+
+
Unlimited projects
+
Priority support
+
Custom domains
+
Advanced analytics
+
+
+
+## Color
+
+Color all markers at once with the `--tblr-list-icon-color` variable on the list. A `text-{color}` utility on one icon colors that marker only.
+
+
+
+
Unlimited projects
+
Priority support
+
Custom domains
+
No phone support
+
+
+
+## Different icons
+
+Each entry can use its own icon. Combine icons and colors to show a status, for example the steps of a setup.
+
+
+
+
Account created
+
Email confirmed
+
Profile in progress
+
Add a payment method
+
+
+
+## Included and excluded
+
+In a pricing table, use a check for what the plan has and a muted cross for what it lacks. Mute the whole entry with `.text-secondary` so the text reads as unavailable too.
+
+
+
+
Unlimited projects
+
Priority support
+
Custom domains
+
Advanced analytics
+
+
+
+## Inline
+
+Add `.list-icon-inline` to show short entries in a row that wraps, for example the highlights under a product name.
+
+
+
+
Free shipping
+
30-day returns
+
2-year warranty
+
In stock
+
+
+
+## Long entries
+
+When an entry wraps to more lines, the marker stays on the first line. The marker follows the line height of the list, so it also fits small or large text.
+
+
+
+
Two-factor authentication keeps the account safe even when the password leaks.
+
Backups run every night and are kept for thirty days in a separate region.
+
Support answers within one business day.
+
+
+
+## Pros and cons
+
+Put two lists side by side to compare: green checks on one side, red crosses on the other.
+
+
+
+
+
+
Fast to set up
+
No vendor lock-in
+
Great documentation
+
+
+
+
+
No built-in auth
+
Smaller community
+
+
+
+
+
+## Entries with a description
+
+An entry can hold more than one line: wrap the content in an element with a title and a muted description. The marker stays next to the title. Raise `--tblr-list-icon-row-gap` to give the entries more room.
+
+
+
+
+
+
+
Unlimited projects
+
Create as many projects as you need, with no extra charge.
+
+
+
+
+
+
Priority support
+
Your questions go to the top of the queue.
+
+
+
+
+
+## Links
+
+The marker can describe the entry instead of ticking it off, for example in a list of contact details with links.
+
+
+
+
+
+## Custom look
+
+Set the `--tblr-list-icon-*` variables on the list to change the size of the markers and the gaps between the entries.
+
+
+
+
Unlimited projects
+
Priority support
+
Custom domains
+
+
+
+## Accessibility
+
+- Use a real list element, `ul` or `ol`, so screen readers announce the number of entries and let the user jump between them.
+- The icons are decorative, so screen readers skip them. When the icon carries meaning, for example a check against a cross, add the meaning as a `.visually-hidden` text, such as "Included" or "Not included".
diff --git a/preview/pages/icon-lists.astro b/preview/pages/icon-lists.astro
new file mode 100644
index 000000000..bc36a1260
--- /dev/null
+++ b/preview/pages/icon-lists.astro
@@ -0,0 +1,337 @@
+---
+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 CardSubtitle from '@ui/CardSubtitle.astro'
+import DocsLink from '@ui/DocsLink.astro'
+import IconList from '@ui/IconList.astro'
+import IconListItem from '@ui/IconListItem.astro'
+
+const features = ['Unlimited projects', 'Priority support', 'Custom domains', 'Advanced analytics']
+
+const layouts = ['Boxed', 'Combined', 'Condensed', 'Fluid', 'Horizontal', 'Vertical', 'RTL mode', 'Dark mode']
+
+const plans = [
+ { name: 'Free', price: '$0', included: 2 },
+ { name: 'Pro', price: '$29', included: 4 },
+]
+
+const overview = [
+ ['Boxed', 'Combined', 'Condensed', 'Fluid', 'Horizontal', 'Vertical'],
+ ['Dark mode', 'RTL mode', 'Navbar sticky', 'Navbar overlap', 'Folded sidebar', 'Auto color mode'],
+]
+
+const pros = ['Fast to set up', 'No vendor lock-in', 'Great documentation']
+const cons = ['No built-in auth', 'Smaller community']
+
+const requirements = [
+ { text: 'At least 8 characters', met: true },
+ { text: 'One upper case letter', met: true },
+ { text: 'One number', met: false },
+ { text: 'One special character', met: false },
+]
+
+const changes = [
+ { icon: 'plus', color: 'green', text: 'Added the icon list component' },
+ { icon: 'arrow-up', color: 'blue', text: 'Faster rendering of large tables' },
+ { icon: 'bug', color: 'red', text: 'Fixed the dropdown position in RTL mode' },
+ { icon: 'trash', color: 'secondary', text: 'Removed the old inline list dots' },
+]
+
+const contact = [
+ { icon: 'mail', text: 'hello@tabler.io', href: 'mailto:hello@tabler.io' },
+ { icon: 'phone', text: '+1 (555) 123-4567', href: 'tel:+15551234567' },
+ { icon: 'map-pin', text: '1 Market Street, San Francisco' },
+ { icon: 'world', text: 'tabler.io', href: 'https://tabler.io' },
+]
+
+const details = [
+ { title: 'Unlimited projects', text: 'Create as many projects as you need, with no extra charge.' },
+ { title: 'Priority support', text: 'Your questions go to the top of the queue.' },
+ { title: 'Custom domains', text: 'Serve every project from your own domain with a free certificate.' },
+]
+
+const steps = [
+ { icon: 'circle-check', color: 'green', text: 'Account created' },
+ { icon: 'circle-check', color: 'green', text: 'Email confirmed' },
+ { icon: 'progress', color: 'blue', text: 'Profile in progress' },
+ { icon: 'circle-dashed', color: 'secondary', text: 'Add a payment method' },
+]
+---
+
+
+
+
+
+
+
+
+ Basic
+ Add .list-icon to a list. The first icon in each entry is the marker.
+
+ {features.map((text) => {text})}
+
+
+
+
+
+
+
+
+ Color
+
+ Color all markers with the --tblr-list-icon-color variable, or one marker with a text-* utility.
+
+
+ {features.map((text) => {text})}
+
+
+
+
+
+
+
+
+ Different icons
+ Each entry can use its own icon and color, for example a status.
+
+ {
+ steps.map((step) => (
+
+ {step.text}
+
+ ))
+ }
+
+
+
+
+
+
+
+
+ Included and excluded
+ A check for what the plan has and a muted cross for what it lacks.
+
+ {
+ features.map((text, i) =>
+ i < 2 ? (
+ {text}
+ ) : (
+
+ {text}
+
+ ),
+ )
+ }
+
+
+
+
+
+
+
+
+ Inline
+
+ Add .list-icon-inline to show short entries in a row that wraps.
+
+
+ {layouts.map((text) => {text})}
+
+
+
+
+
+
+
+
+ Long entries
+ The marker stays on the first line when an entry wraps.
+
+ Two-factor authentication keeps the account safe even when the password leaks.
+ Backups run every night and are kept for thirty days in a separate region.
+ Support answers within one business day, in English and Polish.
+
+
+
+
+
+
+
+
+ Small text
+ The marker follows the line height of the list, so it also fits small or large text.
+
+ {features.map((text) => {text})}
+
+
+
+
+
+
+
+
+ Pros and cons
+ Two lists side by side, green checks against red crosses.
+
+
+
+ {pros.map((text) => {text})}
+
+
+
+
+ {cons.map((text) => {text})}
+
+
+
+
+
+
+
+
+
+
+ Password requirements
+ A checklist under a form field, each rule marked as met or not.
+
+
diff --git a/preview/pages/license.astro b/preview/pages/license.astro
index 279835a2a..0cd0f513e 100644
--- a/preview/pages/license.astro
+++ b/preview/pages/license.astro
@@ -1,6 +1,8 @@
---
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Icon from '@ui/Icon.astro'
+import IconList from '@ui/IconList.astro'
+import IconListItem from '@ui/IconListItem.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import CardFooter from '@ui/CardFooter.astro'
@@ -62,23 +64,23 @@ const licenseHtml = `
Permissions
-
-
Commercial use
-
Modification
-
Distribution
-
Private use
-
+
+ Commercial use
+ Modification
+ Distribution
+ Private use
+
Limitations
-
-
Liability
-
Warranty
-
+
+ Liability
+ Warranty
+
Conditions
-
-
License and copyright notice
-
+
+ License and copyright notice
+
This is not legal advice.
diff --git a/preview/pages/trial-ended.astro b/preview/pages/trial-ended.astro
index 56716878d..2aba7084d 100644
--- a/preview/pages/trial-ended.astro
+++ b/preview/pages/trial-ended.astro
@@ -1,6 +1,7 @@
---
import SingleLayout from '@shared/layouts/SingleLayout.astro'
-import Icon from '@ui/Icon.astro'
+import IconList from '@ui/IconList.astro'
+import IconListItem from '@ui/IconListItem.astro'
---
@@ -10,29 +11,26 @@ import Icon from '@ui/Icon.astro'
If you want to continue to benefit from Tabler, it's time to upgrade your plan.
-
-
-
-
+
+
+ Over 800+ productionsUnlimited movies, TV shows and more.
-
-
-
-
+
+
+ Watch everywhereWatch on smart TVs, PlayStation, Xbox, Apple TV, Blu-ray players and more.
-
-
-
-
+
+
+ PersonalizeChoose your own style, watch what you like.
-
-
+
+
Upgrade to a paid plan
diff --git a/screenshots/pages/icon-list.astro b/screenshots/pages/icon-list.astro
new file mode 100644
index 000000000..93d8197a9
--- /dev/null
+++ b/screenshots/pages/icon-list.astro
@@ -0,0 +1,77 @@
+---
+// Icon list in context: two cards side by side. On the left a pricing plan
+// with green checks for what is included and muted crosses for what is not;
+// on the right a setup checklist where every entry has its own icon, color
+// and a short description. Both show the marker centered on the first line.
+import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
+import Card from '@ui/Card.astro'
+import CardBody from '@ui/CardBody.astro'
+import CardTitle from '@ui/CardTitle.astro'
+import IconList from '@ui/IconList.astro'
+import IconListItem from '@ui/IconListItem.astro'
+
+const plan = [
+ { text: 'Unlimited projects', included: true },
+ { text: 'Up to 10 team members', included: true },
+ { text: 'Custom domains', included: true },
+ { text: 'Priority support', included: true },
+ { text: 'Advanced analytics', included: false },
+ { text: 'Single sign-on', included: false },
+]
+
+const checklist = [
+ { icon: 'circle-check', color: 'green', title: 'Account created', text: 'Your workspace is ready to use.' },
+ { icon: 'circle-check', color: 'green', title: 'Email confirmed', text: 'We sent a link to your inbox.' },
+ { icon: 'progress', color: 'blue', title: 'Team invited', text: '2 of 5 invitations accepted.' },
+ { icon: 'circle-dashed', color: 'secondary', title: 'Add a payment method', text: 'Needed before the trial ends.' },
+]
+---
+
+
+
Choose plan
diff --git a/shared/components/cards/PricingPlan.astro b/shared/components/cards/PricingPlan.astro
index 20bf4c2ed..b2e76cd89 100644
--- a/shared/components/cards/PricingPlan.astro
+++ b/shared/components/cards/PricingPlan.astro
@@ -1,6 +1,7 @@
---
-import Icon from '@ui/Icon.astro'
import Button from '@ui/Button.astro'
+import IconList from '@ui/IconList.astro'
+import IconListItem from '@ui/IconListItem.astro'
---
@@ -11,24 +12,18 @@ import Button from '@ui/Button.astro'
$199
/ month
-
-
-
-
Unlimited Target Audience
-
-
-
-
1 User Account
-
-
-
-
100+ Video Tuts
-
-
-
-
Public Displays
-
-
+
+
+ Unlimited Target Audience
+
+
+ 1 User Account
+
+
+ 100+ Video Tuts
+
+ Public Displays
+
This plan is perfect for small businesses and startups. It includes all the features you need to get started.
diff --git a/shared/components/cards/UserInfo.astro b/shared/components/cards/UserInfo.astro
index d9372f874..2d6b544c7 100644
--- a/shared/components/cards/UserInfo.astro
+++ b/shared/components/cards/UserInfo.astro
@@ -1,5 +1,6 @@
---
-import Icon from '@ui/Icon.astro'
+import IconList from '@ui/IconList.astro'
+import IconListItem from '@ui/IconListItem.astro'
import type { FlagCountry } from '@shared/lib/tokens'
import { people } from '@shared/lib/people'
import Flag from '@ui/Flag.astro'
@@ -12,29 +13,25 @@ const person = requireIndex(people, 24)
Basic info
-
-
- Went to: {person.university}
-
-
-
- Worked at: {person.company}
-
-
-
- Lives in: {person.city}, {person.country}
-
-
-
- From: {person.country}
-
-
-
- Birth date: {person.birth_date}
-
-
-
- Time zone: {person.time_zone}
-
+
+
+ Went to: {person.university}
+
+
+ Worked at: {person.company}
+
+
+ Lives in: {person.city}, {person.country}
+
+
+ From: {person.country}
+
+
+ Birth date: {person.birth_date}
+
+
+ Time zone: {person.time_zone}
+
+