Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.17.0 · main

Grid and layout

tier 2 — classes

Three primitives for placing markup you write: a wrapping grid of peers, a 24-column grid for panels, and a main column beside an aside. None of them cares what its children are — a card, a field, a figure or an element of your own. All three are breakpoint-free: they respond to the width they are given, so the same markup is right on a page, in a drawer and inside a narrow card.

Which one Children that are all the same kind and size — tiles, fields, thumbnails — go in .sedna-grid. Panels of different widths that you place yourself — a dashboard — go in .sedna-grid-24. One content column with a side panel is .sedna-split. A column of panels inside any of them is a .sedna-stack. Page width needs no wrapper: .page holds content to --page-max.

A dashboard

A .stat-row across the top, then two independent columns: a .sedna-span-16 and a .sedna-span-8, each a .sedna-stack of panels. A panel growing in one column moves nothing in the other, which is what you want when the columns are unrelated — the main chart and table on one side, the queue on the other. A stack takes the grid's own gap.

Resize the window to watch it collapse: the two columns stack below 48rem.

Dispatched this week 1,204 8% on last week
Awaiting stock 37 of 1,204 orders
Median time to dispatch 4.2h 20 min faster

Open orders by region

EU-Central 1 240
EU-West 769
Nordics 384
Iberia 223

Latest orders

OrderShort descriptionPriorityAge
ORD-4209Outlook will not start after updateP34 min
ORD-4204VPN disconnects every few minutesP122 min
ORD-4198Request: second monitorP41 h

Needs attention

Queues

EU-West12
EU-Central25
Nordics4
Iberia0
EU-Central counts may lag by ten minutes.
<div class="sedna-grid-24">
    <div class="stat-row">
        <div class="stat">
            <span class="stat-label">Dispatched this week</span>
            <span class="stat-value">1,204</span>
            <span class="stat-delta stat-delta--good"><i class="ri-arrow-up-line"></i> 8% on last week</span>
        </div>
        <div class="stat">
            <span class="stat-label">Awaiting stock</span>
            <span class="stat-value">37</span>
            <span class="stat-target">of 1,204 orders</span>
        </div>
        <div class="stat">
            <span class="stat-label">Median time to dispatch</span>
            <span class="stat-value">4.2<span class="stat-unit">h</span></span>
            <span class="stat-delta stat-delta--good"><i class="ri-arrow-down-line"></i> 20 min faster</span>
        </div>
    </div>

    <div class="sedna-span-16 sedna-stack">
        <section class="card">
            <div class="card-head"><h3>Open orders by region</h3></div>
            <div class="card-body">
                <div class="bar-list">
                    <div class="bar-list-row">
                        <span class="bar-list-label">EU-Central</span>
                        <span class="bar-list-track"><span class="bar-list-fill" style="--value: 100%"></span></span>
                        <span class="bar-list-value">1 240</span>
                    </div>
                    <div class="bar-list-row">
                        <span class="bar-list-label">EU-West</span>
                        <span class="bar-list-track"><span class="bar-list-fill" style="--value: 62%"></span></span>
                        <span class="bar-list-value">769</span>
                    </div>
                    <div class="bar-list-row">
                        <span class="bar-list-label">Nordics</span>
                        <span class="bar-list-track"><span class="bar-list-fill" style="--value: 31%"></span></span>
                        <span class="bar-list-value">384</span>
                    </div>
                    <div class="bar-list-row">
                        <span class="bar-list-label">Iberia</span>
                        <span class="bar-list-track"><span class="bar-list-fill" style="--value: 18%"></span></span>
                        <span class="bar-list-value">223</span>
                    </div>
                </div>
            </div>
            <div class="card-foot card-foot--start"><a href="#">All regions</a></div>
        </section>

        <section class="card">
            <div class="card-head"><h3>Latest orders</h3></div>
            <div class="sedna-scroll-x">
                <table class="table">
                    <thead>
                        <tr><th>Order</th><th>Short description</th><th>Priority</th><th class="text-end">Age</th></tr>
                    </thead>
                    <tbody>
                        <tr><td>ORD-4209</td><td>Outlook will not start after update</td><td><span class="badge badge-warn">P3</span></td><td class="col-muted text-end">4 min</td></tr>
                        <tr><td>ORD-4204</td><td>VPN disconnects every few minutes</td><td><span class="badge badge-danger">P1</span></td><td class="col-muted text-end">22 min</td></tr>
                        <tr><td>ORD-4198</td><td>Request: second monitor</td><td><span class="badge">P4</span></td><td class="col-muted text-end">1 h</td></tr>
                    </tbody>
                </table>
            </div>
        </section>
    </div>

    <div class="sedna-span-8 sedna-stack">
        <section class="card">
            <div class="card-head"><h3>Needs attention</h3></div>
            <ul class="list">
                <li>
                    <a class="list-row" href="#">
                        <span class="list-main">
                            <span class="list-title">VPN disconnects every few minutes</span>
                            <span class="list-sub">ORD-4204 · EU-West</span>
                        </span>
                        <span class="list-meta"><span class="badge badge-danger">P1</span></span>
                    </a>
                </li>
                <li>
                    <a class="list-row" href="#">
                        <span class="list-main">
                            <span class="list-title">Outlook will not start after the update</span>
                            <span class="list-sub">ORD-4209 · EU-Central</span>
                        </span>
                        <span class="list-meta"><span class="badge badge-warn">P3</span></span>
                    </a>
                </li>
                <li>
                    <a class="list-row" href="#">
                        <span class="list-main">
                            <span class="list-title">Label printer offline</span>
                            <span class="list-sub">ORD-4211 · Nordics</span>
                        </span>
                        <span class="list-meta"><span class="badge badge-warn">P3</span></span>
                    </a>
                </li>
            </ul>
            <div class="card-foot"><button class="btn btn-sm" type="button">Open queue</button></div>
        </section>

        <section class="card">
            <div class="card-head"><h3>Queues</h3></div>
            <div class="card-body">
                <div class="kv"><span class="k">EU-West</span><span class="v">12</span></div>
                <div class="kv"><span class="k">EU-Central</span><span class="v">25</span></div>
                <div class="kv"><span class="k">Nordics</span><span class="v">4</span></div>
                <div class="kv"><span class="k">Iberia</span><span class="v">0</span></div>
            </div>
            <div class="card-warning"><i class="ri-error-warning-line"></i> EU-Central counts may lag by ten minutes.</div>
        </section>
    </div>
</div>

One tall panel

A grid row is as tall as its tallest panel. One long list beside three short tiles leaves holes beside the list and pushes the next row down by the difference. There are four ways out, from the smallest change to the biggest.

  • .sedna-self-start on the short panels keeps their own height. The row is still as tall.
  • .sedna-row-span-2, .sedna-row-span-3 and .sedna-row-span-4 let the tall panel take several rows, and the short ones fill the rows beside it.
  • .sedna-stack makes each column independent, as in the dashboard above.
  • .sedna-grid-24--rows gives every row the same height, so nothing can move at all.

.sedna-row-span-2

The rota is two rows tall and the four queues fill the two rows beside it, two by two. .card--fill on every panel makes each end on its row's line.

On call this week

  • AFAlex FischerNetwork · until Friday
  • PNPriya NairFulfilment · until Friday
  • JWJordan WeissDatabase · until Monday
  • RNRin NakamuraStorage · until Monday
EU-West Live
Waiting12
EU-Central Degraded
Waiting25
Nordics Live
Waiting4
Iberia Idle
Nothing waiting
<div class="sedna-grid-24">
    <section class="card card--fill sedna-span-8 sedna-row-span-2">
        <div class="card-head"><h3>On call this week</h3></div>
        <ul class="list">
            <li><div class="list-row"><span class="avatar avatar-sm">AF</span><span class="list-main"><span class="list-title">Alex Fischer</span><span class="list-sub">Network · until Friday</span></span></div></li>
            <li><div class="list-row"><span class="avatar avatar-sm avatar--viz-2">PN</span><span class="list-main"><span class="list-title">Priya Nair</span><span class="list-sub">Fulfilment · until Friday</span></span></div></li>
            <li><div class="list-row"><span class="avatar avatar-sm avatar--viz-3">JW</span><span class="list-main"><span class="list-title">Jordan Weiss</span><span class="list-sub">Database · until Monday</span></span></div></li>
            <li><div class="list-row"><span class="avatar avatar-sm avatar--viz-4">RN</span><span class="list-main"><span class="list-title">Rin Nakamura</span><span class="list-sub">Storage · until Monday</span></span></div></li>
        </ul>
        <div class="card-foot card-foot--start"><a href="#">Full rota</a></div>
    </section>
    <section class="card card--fill sedna-span-8">
        <div class="card-head"><strong>EU-West</strong><span class="health-badge health-badge--live"><span class="health-dot"></span> Live</span></div>
        <div class="card-body"><div class="kv"><span class="k">Waiting</span><span class="v">12</span></div></div>
    </section>
    <section class="card card--fill sedna-span-8">
        <div class="card-head"><strong>EU-Central</strong><span class="health-badge health-badge--degraded"><span class="health-dot"></span> Degraded</span></div>
        <div class="card-body"><div class="kv"><span class="k">Waiting</span><span class="v">25</span></div></div>
    </section>
    <section class="card card--fill sedna-span-8">
        <div class="card-head"><strong>Nordics</strong><span class="health-badge health-badge--live"><span class="health-dot"></span> Live</span></div>
        <div class="card-body"><div class="kv"><span class="k">Waiting</span><span class="v">4</span></div></div>
    </section>
    <section class="card card--fill sedna-span-8">
        <div class="card-head"><strong>Iberia</strong><span class="health-badge health-badge--idle"><span class="health-dot"></span> Idle</span></div>
        <div class="card-body sedna-place-center"><span class="text-muted">Nothing waiting</span></div>
    </section>
</div>

.sedna-grid-24--rows

A board of tiles: every row is --grid-row (9rem) tall whatever is in it, so a panel's height is its row span and nothing else. That is the layout monitoring dashboards use, and the only one in which a panel filling up moves nothing. What does not fit scrolls inside a .card--fill's body while its head and foot stay in view — the queue here has more items than its two rows show. Put a list in a .card-body--flush so it is the body that scrolls.

Dispatched today 214 6%
Awaiting stock 37 of 1,204 orders
Returns 9 3 on yesterday
Median dispatch 4.2h

Open orders by region

EU-Central 1 240
EU-West 769
Nordics 384
Iberia 223

Needs attention

<div class="sedna-grid-24 sedna-grid-24--rows">
    <section class="card card--fill sedna-span-6">
        <div class="card-body">
            <div class="stat">
                <span class="stat-label">Dispatched today</span>
                <span class="stat-value">214</span>
                <span class="stat-delta stat-delta--good"><i class="ri-arrow-up-line"></i> 6%</span>
            </div>
        </div>
    </section>
    <section class="card card--fill sedna-span-6">
        <div class="card-body">
            <div class="stat">
                <span class="stat-label">Awaiting stock</span>
                <span class="stat-value">37</span>
                <span class="stat-target">of 1,204 orders</span>
            </div>
        </div>
    </section>
    <section class="card card--fill sedna-span-6">
        <div class="card-body">
            <div class="stat">
                <span class="stat-label">Returns</span>
                <span class="stat-value">9</span>
                <span class="stat-delta stat-delta--bad"><i class="ri-arrow-up-line"></i> 3 on yesterday</span>
            </div>
        </div>
    </section>
    <section class="card card--fill sedna-span-6">
        <div class="card-body">
            <div class="stat">
                <span class="stat-label">Median dispatch</span>
                <span class="stat-value">4.2<span class="stat-unit">h</span></span>
            </div>
        </div>
    </section>

    <section class="card card--fill sedna-span-16 sedna-row-span-2">
        <div class="card-head"><h3>Open orders by region</h3></div>
        <div class="card-body">
            <div class="bar-list">
                <div class="bar-list-row">
                    <span class="bar-list-label">EU-Central</span>
                    <span class="bar-list-track"><span class="bar-list-fill" style="--value: 100%"></span></span>
                    <span class="bar-list-value">1 240</span>
                </div>
                <div class="bar-list-row">
                    <span class="bar-list-label">EU-West</span>
                    <span class="bar-list-track"><span class="bar-list-fill" style="--value: 62%"></span></span>
                    <span class="bar-list-value">769</span>
                </div>
                <div class="bar-list-row">
                    <span class="bar-list-label">Nordics</span>
                    <span class="bar-list-track"><span class="bar-list-fill" style="--value: 31%"></span></span>
                    <span class="bar-list-value">384</span>
                </div>
                <div class="bar-list-row">
                    <span class="bar-list-label">Iberia</span>
                    <span class="bar-list-track"><span class="bar-list-fill" style="--value: 18%"></span></span>
                    <span class="bar-list-value">223</span>
                </div>
            </div>
        </div>
    </section>

    <section class="card card--fill sedna-span-8 sedna-row-span-2">
        <div class="card-head"><h3>Needs attention</h3></div>
        <div class="card-body card-body--flush">
            <ul class="list">
                <li><a class="list-row" href="#"><span class="list-main"><span class="list-title">VPN disconnects every few minutes</span><span class="list-sub">ORD-4204 · EU-West</span></span><span class="list-meta"><span class="badge badge-danger">P1</span></span></a></li>
                <li><a class="list-row" href="#"><span class="list-main"><span class="list-title">Outlook will not start after the update</span><span class="list-sub">ORD-4209 · EU-Central</span></span><span class="list-meta"><span class="badge badge-warn">P3</span></span></a></li>
                <li><a class="list-row" href="#"><span class="list-main"><span class="list-title">Label printer offline</span><span class="list-sub">ORD-4211 · Nordics</span></span><span class="list-meta"><span class="badge badge-warn">P3</span></span></a></li>
                <li><a class="list-row" href="#"><span class="list-main"><span class="list-title">Request: second monitor</span><span class="list-sub">ORD-4198 · Iberia</span></span><span class="list-meta"><span class="badge">P4</span></span></a></li>
                <li><a class="list-row" href="#"><span class="list-main"><span class="list-title">Scanner battery will not charge</span><span class="list-sub">ORD-4215 · EU-West</span></span><span class="list-meta"><span class="badge">P4</span></span></a></li>
            </ul>
        </div>
        <div class="card-foot"><button class="btn btn-sm" type="button">Open queue</button></div>
    </section>
</div>

.sedna-grid-24--dense

Two 16-column panels leave an 8-column hole beside each. --dense fills it with the next panel that fits, so 3 moves up beside 1. The markup order is still the reading and tab order, so a dense grid reads out of visual order: use it for tiles that are peers, never for a sequence.

1 · sedna-span-16
2 · sedna-span-16
3 · sedna-span-8, moved up beside 1
4 · sedna-span-8, beside 2
<div class="sedna-grid-24 sedna-grid-24--dense sedna-gap-1">
    <div class="card sedna-span-16"><div class="card-body"><strong>1</strong> · <code>sedna-span-16</code></div></div>
    <div class="card sedna-span-16"><div class="card-body"><strong>2</strong> · <code>sedna-span-16</code></div></div>
    <div class="card sedna-span-8"><div class="card-body"><strong>3</strong> · <code>sedna-span-8</code>, moved up beside 1</div></div>
    <div class="card sedna-span-8"><div class="card-body"><strong>4</strong> · <code>sedna-span-8</code>, beside 2</div></div>
</div>

.sedna-grid-24

Twenty-four columns. .sedna-span-1 to .sedna-span-24 give a child that many of them, and a child with no span takes the whole row. The even steps — .sedna-span-2, -4, -6-24 — are a twelve-column grid, if twelfths are how you think; the odd ones are there for the layouts twelve cannot divide.

It collapses on its own width, not the viewport's. Narrower than 48rem, a span under 8 becomes 12, so tiles pair up, and a span of 8 or more takes the row. Narrower than 30rem, everything takes the row. A .sedna-grid-24 inside a span collapses by its own width. It is a size container, so it takes its width from where it sits: inside a flex row, give it .sedna-fill.

24
1
23
2
22
3
21
4
20
5
19
6
18
7
17
8
16
9
15
10
14
11
13
12
12
<div class="sedna-grid-24 sedna-gap-1">
    <div class="card sedna-span-24"><div class="card-body text-center"><code>24</code></div></div>
    <div class="card sedna-span-1"><div class="card-body text-center"><code>1</code></div></div>
    <div class="card sedna-span-23"><div class="card-body text-center"><code>23</code></div></div>
    <div class="card sedna-span-2"><div class="card-body text-center"><code>2</code></div></div>
    <div class="card sedna-span-22"><div class="card-body text-center"><code>22</code></div></div>
    <div class="card sedna-span-3"><div class="card-body text-center"><code>3</code></div></div>
    <div class="card sedna-span-21"><div class="card-body text-center"><code>21</code></div></div>
    <div class="card sedna-span-4"><div class="card-body text-center"><code>4</code></div></div>
    <div class="card sedna-span-20"><div class="card-body text-center"><code>20</code></div></div>
    <div class="card sedna-span-5"><div class="card-body text-center"><code>5</code></div></div>
    <div class="card sedna-span-19"><div class="card-body text-center"><code>19</code></div></div>
    <div class="card sedna-span-6"><div class="card-body text-center"><code>6</code></div></div>
    <div class="card sedna-span-18"><div class="card-body text-center"><code>18</code></div></div>
    <div class="card sedna-span-7"><div class="card-body text-center"><code>7</code></div></div>
    <div class="card sedna-span-17"><div class="card-body text-center"><code>17</code></div></div>
    <div class="card sedna-span-8"><div class="card-body text-center"><code>8</code></div></div>
    <div class="card sedna-span-16"><div class="card-body text-center"><code>16</code></div></div>
    <div class="card sedna-span-9"><div class="card-body text-center"><code>9</code></div></div>
    <div class="card sedna-span-15"><div class="card-body text-center"><code>15</code></div></div>
    <div class="card sedna-span-10"><div class="card-body text-center"><code>10</code></div></div>
    <div class="card sedna-span-14"><div class="card-body text-center"><code>14</code></div></div>
    <div class="card sedna-span-11"><div class="card-body text-center"><code>11</code></div></div>
    <div class="card sedna-span-13"><div class="card-body text-center"><code>13</code></div></div>
    <div class="card sedna-span-12"><div class="card-body text-center"><code>12</code></div></div>
    <div class="card sedna-span-12"><div class="card-body text-center"><code>12</code></div></div>
</div>

.sedna-split

A main column and an aside. The child marked .sedna-split-aside takes --split-aside (18rem; .sedna-split--sm is 14rem, .sedna-split--lg is 24rem), and the rest share what is left. The aside goes on whichever side it sits in the markup. When the main column would drop below --split-main (60%), the two stack.

ORD-4204 · VPN disconnects every few minutes

The client drops the tunnel roughly every four minutes and reconnects on its own. It started after the last policy update and affects everyone on the EU-West gateway.

Workaround until the fix is rolled out: reconnect manually, or use the EU-Central gateway.

<div class="sedna-split">
    <div class="card">
        <div class="card-head"><h3>ORD-4204 · VPN disconnects every few minutes</h3></div>
        <div class="card-body">
            <p>The client drops the tunnel roughly every four minutes and reconnects on its own. It started after
            the last policy update and affects everyone on the EU-West gateway.</p>
            <p>Workaround until the fix is rolled out: reconnect manually, or use the EU-Central gateway.</p>
        </div>
    </div>
    <aside class="card sedna-split-aside">
        <div class="card-head"><strong>Details</strong></div>
        <div class="card-body">
            <div class="kv"><span class="k">Priority</span><span class="badge badge-danger">P1</span></div>
            <div class="kv"><span class="k">Region</span><span class="v">EU-West</span></div>
            <div class="kv"><span class="k">Owner</span><span>Alex Fischer</span></div>
            <div class="kv"><span class="k">Age</span><span class="v">22 min</span></div>
        </div>
    </aside>
</div>

.sedna-grid

Peers in as many columns as --grid-min (280px) allows. It fills with empty tracks, so three children in a wide grid stay three tracks wide instead of stretching across the row. .sedna-grid--sm sets the minimum to 200px and .sedna-grid--lg to 340px.

QueueFulfilment
Waiting12
QueueNetwork
Waiting3
QueueOn call
Waiting0
<div class="sedna-grid">
    <div class="card">
        <div class="card-body">
            <div class="kv"><span class="k">Queue</span><span class="v">Fulfilment</span></div>
            <div class="kv"><span class="k">Waiting</span><span class="v">12</span></div>
        </div>
    </div>
    <div class="card">
        <div class="card-body">
            <div class="kv"><span class="k">Queue</span><span class="v">Network</span></div>
            <div class="kv"><span class="k">Waiting</span><span class="v">3</span></div>
        </div>
    </div>
    <div class="card">
        <div class="card-body">
            <div class="kv"><span class="k">Queue</span><span class="v">On call</span></div>
            <div class="kv"><span class="k">Waiting</span><span class="v">0</span></div>
        </div>
    </div>
</div>

.sedna-grid--fit

With --fit the empty tracks collapse, so the children that are there stretch to fill the row. With .sedna-start added, each child keeps its own height instead of stretching to the tallest in its row, which is what cards of very different lengths want.

Appearance
Notifications
Account
Signed in asalex.fischer
Roleadmin
<div class="sedna-grid sedna-grid--fit sedna-grid--lg sedna-start">
    <div class="card">
        <div class="card-head"><strong>Appearance</strong></div>
        <div class="card-body" style="display:flex; flex-direction:column; gap:10px">
            <label class="form-check"><input type="checkbox" /> Light theme</label>
            <label class="form-check"><input type="checkbox" /> Colour-blind palette</label>
            <label class="form-check"><input type="checkbox" checked /> Compact tables</label>
        </div>
    </div>
    <div class="card">
        <div class="card-head"><strong>Notifications</strong></div>
        <div class="card-body" style="display:flex; flex-direction:column; gap:10px">
            <label class="form-check"><input type="checkbox" checked /> Desktop notifications</label>
            <label class="form-check"><input type="checkbox" /> Sound</label>
        </div>
    </div>
    <div class="card">
        <div class="card-head"><strong>Account</strong></div>
        <div class="card-body">
            <div class="kv"><span class="k">Signed in as</span><span class="v">alex.fischer</span></div>
            <div class="kv"><span class="k">Role</span><span class="v">admin</span></div>
        </div>
    </div>
</div>

Fields side by side

.sedna-grid sedna-grid--sm sedna-grid--fit is the form row: fields side by side, dropping to one column below about 200px each. Use it for values that belong together, like a from/to pair or a host and port.

<div class="sedna-grid sedna-grid--sm sedna-grid--fit">
    <div class="form-field">
        <label class="form-label" for="g-host">Host</label>
        <input class="form-input" id="g-host" type="text" value="192.0.2.20" />
    </div>
    <div class="form-field">
        <label class="form-label" for="g-port">Port</label>
        <input class="form-input" id="g-port" type="text" value="3306" />
    </div>
    <div class="form-field">
        <label class="form-label" for="g-db">Database</label>
        <input class="form-input" id="g-db" type="text" value="orders" />
    </div>
</div>

.sedna-span-full

One child taking the whole row of any grid: a chart over a row of tiles, the odd card that would otherwise sit alone at one track wide, or the long field among two-up fields. You decide which child that is. CSS cannot count the children against the tracks.

Accounts reconciled

1 204 of 1 211 accounts matched on the last run.

Pending review

Seven accounts differ in cost centre and are held for approval.

Last twelve runs

The odd card out, spanning the row it would otherwise sit alone in. Narrow this window until the grid drops to one column: the span becomes the single track and nothing overflows.
<div class="sedna-grid sedna-grid--fit sedna-grid--lg sedna-start">
    <div class="card">
        <div class="card-head"><h3>Accounts reconciled</h3></div>
        <div class="card-body">1 204 of 1 211 accounts matched on the last run.</div>
    </div>
    <div class="card">
        <div class="card-head"><h3>Pending review</h3></div>
        <div class="card-body">Seven accounts differ in cost centre and are held for approval.</div>
    </div>
    <div class="card sedna-span-full">
        <div class="card-head"><h3>Last twelve runs</h3></div>
        <div class="card-body">
            The odd card out, spanning the row it would otherwise sit alone in. Narrow this window
            until the grid drops to one column: the span becomes the single track and nothing
            overflows.
        </div>
    </div>
</div>
Renamed .card-grid is now .sedna-grid. .card-masonry is now .sedna-grid sedna-grid--fit sedna-grid--lg sedna-start. .field-grid is now .sedna-grid sedna-grid--sm sedna-grid--fit. The old names are gone, not aliased.