Components
Timeline
Events or steps in order, down the page or across it, with a line drawn between their indicators. Each item holds an indicator (an icon, a number, a word) and the content beside it. A list, so there is nothing to hold in C#.
Drawn and behaving as Flux UI's timeline does, measured against its live docs.
Timeline
Events down the page, each beside its indicator.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineExample : Component
{
// Inside a heading a name is the heading's own ink, and what was done is inline text beside it.
protected override Component? Render() =>
Ui.Timeline[
Ui.TimelineItem.Key("review")[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.Eye).Micro],
Ui.TimelineContent[
Ui.Heading["curtisss", Ui.Text.Inline()[" requested a review from "], "james_rob", Ui.Text.Inline()[" · 4 days ago"]]
]
],
Ui.TimelineItem.Key("tag")[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.Tag).Micro],
Ui.TimelineContent[Ui.Heading["curtisss", Ui.Text.Inline()[" added tag "], Ui.Badge.Sm["feature"]]]
],
Ui.TimelineItem.Key("approved")[
Ui.TimelineIndicator.Color(Ui.Color.Green)[Ui.Icon.Name(Ui.IconName.Check).Micro],
Ui.TimelineContent[Ui.Heading["james_rob", Ui.Text.Inline()[" approved these changes · 3 days ago"]]]
]
];
}- curtisss requested a review from james_rob · 4 days ago
- curtisss added tagfeature
- james_rob approved these changes · 3 days ago
Large
48px indicators on a 2px line with wider gaps, for numbered steps.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineLargeExample : Component
{
private static readonly (string Number, string Title, string Detail)[] Steps =
[
("1", "Submit", "Complete the form and provide all necessary assets."),
("2", "Review", "Verify your data and approve the configuration."),
("3", "Publish", "Finalize your settings and push your changes live."),
];
protected override Component? Render() =>
Ui.Timeline.Lg[
Steps.Select(step => Ui.TimelineItem.Key(step.Number)[
Ui.TimelineIndicator[step.Number],
Ui.TimelineContent[Ui.Heading[step.Title], Ui.Text[step.Detail]]
])
];
}- 1Submit
Complete the form and provide all necessary assets.
- 2Review
Verify your data and approve the configuration.
- 3Publish
Finalize your settings and push your changes live.
Horizontal
Indicators in a row, the content under each.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineHorizontalExample : Component
{
protected override Component? Render() =>
Ui.Timeline.Horizontal()[
Ui.TimelineItem.Key("confirmed")[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.CreditCard).Micro],
Ui.TimelineContent[Ui.Heading["Order confirmed"]]
],
Ui.TimelineItem.Key("shipped")[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.Truck).Micro],
Ui.TimelineContent[Ui.Heading["On its way"]]
],
Ui.TimelineItem.Key("delivered")[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.Home).Micro],
Ui.TimelineContent[Ui.Heading["Delivered"]]
]
];
}- Order confirmed
- On its way
- Delivered
Status
Complete is filled with a dark line on to the next, current a dark ring, incomplete a faint ring with its content dimmed.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineStatusExample : Component
{
protected override Component? Render() =>
Ui.Timeline.Horizontal()[
Ui.TimelineItem.Key("confirmed").Complete[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.CreditCard).Micro],
Ui.TimelineContent[Ui.Heading["Order confirmed"]]
],
Ui.TimelineItem.Key("shipped").Current[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.Truck).Micro],
Ui.TimelineContent[Ui.Heading["On its way"]]
],
Ui.TimelineItem.Key("delivered").Incomplete[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.Home).Micro],
Ui.TimelineContent[Ui.Heading["Delivered"]]
]
];
}- Order confirmed
- On its way
- Delivered
Indicator color
A coloured circle in any of the seventeen hues.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineColorExample : Component
{
protected override Component? Render() =>
Ui.Timeline[
Ui.TimelineItem.Key("failed")[
Ui.TimelineIndicator.Color(Ui.Color.Red)[Ui.Icon.Name(Ui.IconName.XMark).Micro],
Ui.TimelineContent[Ui.Heading["Build failed"]]
],
Ui.TimelineItem.Key("warning")[
Ui.TimelineIndicator.Color(Ui.Color.Amber)[Ui.Icon.Name(Ui.IconName.ExclamationTriangle).Micro],
Ui.TimelineContent[Ui.Heading["Warning issued"]]
],
Ui.TimelineItem.Key("deployed")[
Ui.TimelineIndicator.Color(Ui.Color.Green)[Ui.Icon.Name(Ui.IconName.Check).Micro],
Ui.TimelineContent[Ui.Heading["Deployed successfully"]]
]
];
}- Build failed
- Warning issued
- Deployed successfully
Bare indicator
No circle and no size: a larger icon standing on its own.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineBareExample : Component
{
private static readonly (Ui.IconName Icon, string Title, string When)[] Events =
[
(Ui.IconName.DocumentText, "Draft created", " · 2 days ago"),
(Ui.IconName.PencilSquare, "Edits made", " · 1 day ago"),
(Ui.IconName.PaperAirplane, "Published", " · just now"),
];
protected override Component? Render() =>
Ui.Timeline[
Events.Select(e => Ui.TimelineItem.Key(e.Title)[
Ui.TimelineIndicator.Bare[Ui.Icon.Name(e.Icon).Class("size-6 text-zinc-400")],
Ui.TimelineContent[Ui.Heading[e.Title, Ui.Text.Inline()[e.When]]]
])
];
}- Draft created · 2 days ago
- Edits made · 1 day ago
- Published · just now
Block item
A callout as an item, spanning the timeline's width with the line running into it and out again.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineBlockExample : Component
{
protected override Component? Render() =>
Ui.Timeline[
Ui.TimelineItem.Key("review")[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.Eye).Micro],
Ui.TimelineContent[Ui.Heading["curtisss", Ui.Text.Inline()[" requested a review from "], "james_rob"]]
],
Ui.TimelineItem.Key("reply")[
Ui.TimelineBlock[
Ui.Callout.Secondary.Actions([Ui.Button["View message ->"], Ui.Button.Ghost["Reply"]])[
Ui.CalloutHeading["james_rob", Ui.Text["replied to your message · 4 days ago"]]
]
]
],
Ui.TimelineItem.Key("approved")[
Ui.TimelineIndicator.Color(Ui.Color.Green)[Ui.Icon.Name(Ui.IconName.Check).Micro],
Ui.TimelineContent[Ui.Heading["james_rob", Ui.Text.Inline()[" approved these changes · 3 days ago"]]]
]
];
}- curtisss requested a review from james_rob
- james_rob
replied to your message · 4 days ago
- james_rob approved these changes · 3 days ago
Block subgrid
A comment thread whose avatars line up with the indicators around it.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineSubgridExample : Component
{
// The avatars are plain initials in a circle; each subgrid puts its first child in the indicators' column.
protected override Component? Render() =>
Ui.Timeline[
Ui.TimelineItem.Key("review")[
Ui.TimelineIndicator[Ui.Icon.Name(Ui.IconName.Eye).Micro],
Ui.TimelineContent[Ui.Heading["curtisss", Ui.Text.Inline()[" requested a review from "], "james_rob"]]
],
Ui.TimelineItem.Key("thread")[
Ui.TimelineBlock.Class("overflow-hidden rounded-xl border border-zinc-500/20")[
Ui.TimelineSubgrid.Class("bg-zinc-500/5 p-3")[
Ui.Avatar.Xs.Circle().Initials("JR"),
Div.Class("space-y-1")[
Ui.Heading["james_rob", Ui.Text.Inline().Class("font-medium")[" commented · 4 days ago"]],
Ui.Text.Class("text-zinc-800 dark:text-white")["Contrast slider goes a bit wild at the top end..."]
]
],
Div.Class("border-t border-zinc-500/20"),
Ui.TimelineSubgrid.Class("p-3")[Ui.Avatar.Xs.Circle().Initials("ML"), Ui.Text["Leave a reply..."]]
]
],
Ui.TimelineItem.Key("approved")[
Ui.TimelineIndicator.Color(Ui.Color.Green)[Ui.Icon.Name(Ui.IconName.Check).Micro],
Ui.TimelineContent[Ui.Heading["james_rob", Ui.Text.Inline()[" approved these changes · 3 days ago"]]]
]
];
}- curtisss requested a review from james_rob
- JRjames_rob commented · 4 days ago
Contrast slider goes a bit wild at the top end...
MLLeave a reply...
- james_rob approved these changes · 3 days ago
Alignment
Where the content sits beside its indicator: start, baseline, centre or end.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineAlignExample : Component
{
private static readonly (Ui.TimelineAlign Align, string Number, string Title, string Detail)[] Items =
[
(Ui.TimelineAlign.Start, "1", "Start align", "Align indicator and content to the top/start"),
(Ui.TimelineAlign.Baseline, "2", "Baseline align", "Align indicator and content to the baseline of the first line of text"),
(Ui.TimelineAlign.Center, "3", "Center align", "Align indicator and content to the center"),
(Ui.TimelineAlign.End, "4", "End align", "Align indicator and content to the bottom/end"),
];
protected override Component? Render() =>
Ui.Timeline.Class("[--ui-timeline-item-gap:1.5rem]")[
Items.Select(item => Ui.TimelineItem.Key(item.Number).Align(item.Align)[
Ui.TimelineIndicator[item.Number],
Ui.TimelineContent.Class("gap-1")[Ui.Heading[item.Title], Ui.Text[item.Detail]]
])
];
}- 1Start align
Align indicator and content to the top/start
- 2Baseline align
Align indicator and content to the baseline of the first line of text
- 3Center align
Align indicator and content to the center
- 4End align
Align indicator and content to the bottom/end
Baseline adjustment
Give the indicator's hidden first line the heading's size, and the two baselines meet.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineBaselineExample : Component
{
// The indicator's hidden first line takes the heading's size, so the two baselines meet.
protected override Component? Render() =>
Ui.Timeline.Lg[
Ui.TimelineItem.Key("without").Baseline[
Ui.TimelineIndicator["1"],
Ui.TimelineContent.Class("gap-1")[
Ui.Heading.Xl["Without adjustment"],
Ui.Text["The indicator may not align perfectly with large text"]
]
],
Ui.TimelineItem.Key("with").Baseline.Class("[&_[data-ui-timeline-baseline]]:text-2xl")[
Ui.TimelineIndicator["2"],
Ui.TimelineContent.Class("gap-1")[
Ui.Heading.Xl["With baseline adjustment"],
Ui.Text["The indicator aligns precisely with the heading baseline"]
]
]
];
}- 1Without adjustment
The indicator may not align perfectly with large text
- 2With baseline adjustment
The indicator aligns precisely with the heading baseline
Spacing
Two CSS variables: the gap between items, and between an indicator and its content.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimelineSpacingExample : Component
{
protected override Component? Render() =>
Ui.Timeline.Class("[--ui-timeline-item-gap:3rem] [--ui-timeline-content-gap:1rem]")[
Ui.TimelineItem.Key("1")[Ui.TimelineIndicator["1"], Ui.TimelineContent[Ui.Heading["Create account"]]],
Ui.TimelineItem.Key("2")[Ui.TimelineIndicator["2"], Ui.TimelineContent[Ui.Heading["Provide details"]]],
Ui.TimelineItem.Key("3")[Ui.TimelineIndicator["3"], Ui.TimelineContent[Ui.Heading["Review & confirm"]]]
];
}- 1Create account
- 2Provide details
- 3Review & confirm
Behaviour
Indicators and status
Step | What happens |
|---|---|
Ui.Timeline.Horizontal() | Across the page: indicators in a row, the content under each. |
Ui.Timeline.Lg | 48px indicators on a 2px line with wider gaps. Ui.TimelineItem.Lg enlarges one indicator. |
.Start .Baseline .Center .End | Where the content sits beside its indicator (Ui.TimelineAlign, centre by default): on the timeline for every item, on an item for itself. |
.Complete .Current .Incomplete | Progress (Ui.TimelineStatus): a filled indicator and a dark line on to the next item; a dark ring; a faint ring with the content dimmed. Ui.TimelineIndicator.Status(…) overrides the item's for the indicator alone. |
Ui.TimelineIndicator.Color(Ui.Color.Red) | A coloured circle, in any of the seventeen hues. A status is drawn instead of a colour. |
Ui.TimelineIndicator.Bare | No circle and no size: a larger icon standing on its own. |
A block instead of an indicator
Ui.TimelineBlock makes an item of a card or a callout, spanning the timeline's width with the line running into it and out again. Inside one, Ui.TimelineSubgrid puts its first child back in the indicators' column and its second in the content's: a comment thread whose avatars line up with the events around it.
Baseline alignment
Baseline alignment lines the content's first line up with the indicator's text. An indicator holding an icon has no text, so every indicator carries a hidden, empty first line (data-ui-timeline-baseline) to stand in for it. Give that line the content's font size when it is not the default: Ui.TimelineItem.Baseline.Class("[&_[data-ui-timeline-baseline]]:text-2xl").
Spacing
Spacing is two CSS variables on the timeline: --ui-timeline-item-gap between items and --ui-timeline-content-gap between an indicator and its content, Flux's --flux-timeline-item-gap and --flux-timeline-content-gap.
Reference
Ui.Timeline
Events or steps in order, with a line drawn between their indicators.
Step | Takes | What it does |
|---|---|---|
.Horizontal() | bool | Across the page rather than down it: indicators in a row, the content under each. |
.Align(…) | Ui.TimelineAlignCenter Start Baseline End Each is a step of its own too, unless an HTML tag has its name. | Where every item's content sits beside its indicator. Centred when unset. |
.Size(…) | Ui.TimelineSizeBase Lg Each is a step of its own too, unless an HTML tag has its name. | The size of the indicators, and with it the line and the gaps. |
Ui.TimelineItem
One event: an indicator and its content, or a block.
Step | Takes | What it does |
|---|---|---|
.Status(…) | Ui.TimelineStatusDefault Complete Current Incomplete Each is a step of its own too, unless an HTML tag has its name. | How far along it is: it draws the indicator, and a complete item darkens the line on to the next. |
.Align(…) | Ui.TimelineAlignCenter Start Baseline End Each is a step of its own too, unless an HTML tag has its name. | Where this item's content sits, when not where the timeline puts it. |
.Size(…) | Ui.TimelineSizeBase Lg Each is a step of its own too, unless an HTML tag has its name. | The size of this item's indicator, when not the timeline's. |
Ui.TimelineIndicator
The circle on the line: an icon, a number, a word.
Step | Takes | What it does |
|---|---|---|
.Variant(…) | Ui.TimelineIndicatorVariantDefault Bare Each is a step of its own too, unless an HTML tag has its name. | Bare drops the circle and its size, for a larger icon standing on its own. |
.Status(…) | Ui.TimelineStatusDefault Complete Current Incomplete Each is a step of its own too, unless an HTML tag has its name. | The status to draw, when not the item's. |
.Color(…) | Ui.Color: one of Tailwind's hues, each a step too (.Blue) | A coloured circle. A status is drawn instead of it. |
Ui.TimelineContent
What the item says, beside its indicator.
No steps of its own: what goes inside it is its content.
It is one HTML element, so Id, Class, Style, Data, Aria and the events are its steps too.
Ui.TimelineBlock
A card or a callout spanning the timeline's width, the line running into it and out again.
No steps of its own: what goes inside it is its content.
It is one HTML element, so Id, Class, Style, Data, Aria and the events are its steps too.
Ui.TimelineSubgrid
Inside a block: its first child in the indicators' column, its second in the content's.
No steps of its own: what goes inside it is its content.
It is one HTML element, so Id, Class, Style, Data, Aria and the events are its steps too.