Rask
Rask
v0.23.1-alpha.0.440

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.

TimelineExample.cs
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"]]]
            ]
        ];
}
Live result
  1. curtisss requested a review from james_rob · 4 days ago
  2. curtisss added tag
    feature
  3. james_rob approved these changes · 3 days ago

Large

48px indicators on a 2px line with wider gaps, for numbered steps.

TimelineLargeExample.cs
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]]
            ])
        ];
}
Live result
  1. 1
    Submit

    Complete the form and provide all necessary assets.

  2. 2
    Review

    Verify your data and approve the configuration.

  3. 3
    Publish

    Finalize your settings and push your changes live.

Horizontal

Indicators in a row, the content under each.

TimelineHorizontalExample.cs
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"]]
            ]
        ];
}
Live result
  1. Order confirmed
  2. On its way
  3. 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.

TimelineStatusExample.cs
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"]]
            ]
        ];
}
Live result
  1. Order confirmed
  2. On its way
  3. Delivered

Indicator color

A coloured circle in any of the seventeen hues.

TimelineColorExample.cs
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"]]
            ]
        ];
}
Live result
  1. Build failed
  2. Warning issued
  3. Deployed successfully

Bare indicator

No circle and no size: a larger icon standing on its own.

TimelineBareExample.cs
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]]]
            ])
        ];
}
Live result
  1. Draft created · 2 days ago
  2. Edits made · 1 day ago
  3. Published · just now

Block item

A callout as an item, spanning the timeline's width with the line running into it and out again.

TimelineBlockExample.cs
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"]]]
            ]
        ];
}
Live result
  1. curtisss requested a review from james_rob
  2. james_rob

    replied to your message · 4 days ago

  3. james_rob approved these changes · 3 days ago

Block subgrid

A comment thread whose avatars line up with the indicators around it.

TimelineSubgridExample.cs
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"]]]
            ]
        ];
}
Live result
  1. curtisss requested a review from james_rob
  2. JR
    james_rob commented · 4 days ago

    Contrast slider goes a bit wild at the top end...

    ML

    Leave a reply...

  3. james_rob approved these changes · 3 days ago

Alignment

Where the content sits beside its indicator: start, baseline, centre or end.

TimelineAlignExample.cs
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]]
            ])
        ];
}
Live result
  1. 1
    Start align

    Align indicator and content to the top/start

  2. 2
    Baseline align

    Align indicator and content to the baseline of the first line of text

  3. 3
    Center align

    Align indicator and content to the center

  4. 4
    End 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.

TimelineBaselineExample.cs
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"]
                ]
            ]
        ];
}
Live result
  1. 1
    Without adjustment

    The indicator may not align perfectly with large text

  2. 2
    With 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.

TimelineSpacingExample.cs
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"]]]
        ];
}
Live result
  1. 1
    Create account
  2. 2
    Provide details
  3. 3
    Review & confirm

Behaviour

Indicators and status

Step
What happens
Ui.Timeline.Horizontal()Across the page: indicators in a row, the content under each.
Ui.Timeline.Lg48px indicators on a 2px line with wider gaps. Ui.TimelineItem.Lg enlarges one indicator.
.Start .Baseline .Center .EndWhere the content sits beside its indicator (Ui.TimelineAlign, centre by default): on the timeline for every item, on an item for itself.
.Complete .Current .IncompleteProgress (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.BareNo 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

flux:timeline

Events or steps in order, with a line drawn between their indicators.

Step
Takes
What it does
.Horizontal()boolAcross the page rather than down it: indicators in a row, the content under each.
.Align(…)
Ui.TimelineAlign
Center
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.TimelineSize
Base
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

flux:timeline.item

One event: an indicator and its content, or a block.

Step
Takes
What it does
.Status(…)
Ui.TimelineStatus
Default
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.TimelineAlign
Center
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.TimelineSize
Base
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

flux:timeline.indicator

The circle on the line: an icon, a number, a word.

Step
Takes
What it does
.Variant(…)
Ui.TimelineIndicatorVariant
Default
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.TimelineStatus
Default
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

flux:timeline.content

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

flux:timeline.block

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

flux:timeline.subgrid

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.