footstrap — component gallery

Typography & text elements

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6

Body paragraph with a link, strong, emphasis, inline code, a var, small print, H2O and x2, an abbr, Ctrl+C, deleted and inserted text.

A blockquote, as an app might render a hint.

#!/bin/sh
uci set network.lan.ipaddr='192.168.1.1'
uci commit network

  • Unordered item
  • Another item
  1. Ordered item
  2. Another item
  • Unstyled list item
Term
Definition
Другой термин
Определение
OpenWrt, somewhere on a LAN

Buttons — every cbi-button variant

The colour words (.danger/.error/.success/.info) and size words (.large/.small) come from styles/base, but the theme's plain .btn rule sits in a LATER layer and overrides background/padding/radius — so the theme has to re-state them (theme/55-buttons.css). They rendered as neutral grey buttons, and .btn.large came out with small-button padding, until it did. Nothing in stock LuCI emits them; a third-party luci-app-* can.

Keyboard focus: Tab through this section. Every button must show a ring — there was none at all before (base listed button:hover, not :focus, and the theme's box-shadow: none erased the rest).

Row-action size (inside a .td) is shown in the tables section.

Page action bar

  • Save & Apply
  • Apply unchecked
··· ▾
  • Save & Apply
  • Apply unchecked
··· ▾

Form fields (.cbi-value rows)

Section title

A section description, as CBI renders it.
Expecting: valid IP address

/etc/luci-uploads » certs

Create Cancel
40
192.168.1.1
1.1.1.1
  • A section error, as CBI renders a validation failure.

cbi-dropdown widget (the JS select)

  • Selected value
▾
  • Button dropdown
▾
  • Applying…
▾
  • Автоматический выбор лучшего доступного вышестоящего DNS-сервера по задержке отклика
▾

Closed controls only. The open list — and the create-item row that goes with it — is in the Combobox section below.

Combobox (cbi-dropdown + custom value)

  • -- Please choose --
  • wan
  • lan
  • wg0
··· ▾
  • -- Please choose --
  • wan
  • lan
··· ▾
  • -- Please choose --
  • wan
  • lan
  • wg0
··· ▾

What to look at: the create-item row must read as a text field, not as a choice — it is the only row a user can type in. Base gives it width: 100% via li > input.create-item-input:first-child:last-child, which only matches because the input is that li's sole child; the theme paints it as a normal input (theme/65-dropdown.css). Check its focus ring, and check that the row's own hover pill does not fight the field inside it.

The theme also carries a rule for li.create-item, a class stock LuCI does not emit anywhere (grepped: ui.js gives the li no class at all, only the input gets .create-item-input). It is kept under the coverage contract — a third-party app may still emit it — but it is deliberately not rendered here, because this gallery shows the markup LuCI really produces.

cbi-dropdown, MULTIPLE and open

··· ▾

What to look at: every row is the menu's own face, not the mono a field hands its descendants — the <label> is a LABEL, and mono makes a wordy option list (3G Band 1) read stretched. A chosen row says so with its CHECKBOX, so it does not also wear the accent rail the single-select row above needs: the rail is an inset shadow on a rounded row, i.e. an arc, and a column of them reads as a broken frame. The focused row wears the theme's ring, inset — the browser's own outline: auto is drawn outside the row and the menu clips it.

Tabs

Alerts & notices

Plain alert-message.
notice — a poll or a hint.
info
success
warning — configuration not committed.
danger — the interface will go down.
error

With a heading and actions

Some explanatory body text.

Heading 5 inside an alert

Body.

  • a list item
  • another one
a <pre> block inside an alert

Firmware update available

FieldCurrentAvailable
Version25.12.025.12.1
Size7.8 MB7.9 MB

Tooltips (.cbi-tooltip — the colour words too)

plain .cbi-tooltip
.cbi-tooltip.info
.cbi-tooltip.success
.cbi-tooltip.error
.cbi-tooltip.notice

.cbi-select (the non-JS select fallback)

Progress bars

What to look at: the two shapes read as one row each. In the bare meters the value sits above the bar's right edge, pairing with whatever names the bar on the line above; in a .cbi-value row it sits after the bar, on the row's own line, and the values line up in a column because the reserve is fixed rather than shrink-wrapped.

Badges & status

eth0 br-lan lan empty label notice warning danger success
wan
1000 Mbit/s
▲ 1.2 GiB ▼ 8.3 GiB
lan2
no link
IPv4 Upstream
Protocol: Static address
Address: 203.0.113.42/24
Gateway: 203.0.113.1
DNS: 9.9.9.9
Connected: 8d 12h 0m 57s
Device: Ethernet Adapter: "wwan0"
MAC address: C6:68:C6:D1:EF:25
IPv6 Upstream
Protocol: Static address
Prefix Delegated: 2001:db8:1a2b:3c4e:5e6f:7a8b:9c0d::/64
Address: 2001:db8:1a2b:3c4d:5e6f:7a8b:9c0d:1e2f/64
Gateway: 2001:db8:1a2b:3c4d::1
Connected: 8d 12h 0m 57s
Device: Ethernet Adapter: "wwan0"
MAC address: C6:68:C6:D1:EF:25
Подключение IPv4 (внешняя сеть)
Протокол: Статический адрес
Адрес: 203.0.113.42/24
Шлюз: 203.0.113.1
Соединение: 8d 12h 0m 57s
hover for tooltipThe tooltip body. Loading…

Key/value table (no id) — System, Memory

System

Hostname
OpenWrt
Model
Netcore N60 Pro
Uptime
8d 14h 11m 18s

Data table (with id) — leases, processes

On a live page fs-select.js measures this one and cards it when it stops fitting. The gallery has no LuCI JS, so it is shown in both states: the table in a scroll frame (gallery chrome, so a narrow viewport does not drag the whole page sideways), and the card below it.

Active DHCPv4 Leases

HostnameIPv4 address MACLease time 
Watch10.11.12.131 E6:EF:BA:54:DE:329h 10m 51s
iPhone10.11.12.182 F4:A3:10:96:E0:538h 56m 4s
nb-ivan10.11.12.144 A2:9C:41:07:BB:102h 28m 11s

…and the same table carded (.fs-stacked)

HostnameIPv4 address MACLease time 
Watch10.11.12.131 E6:EF:BA:54:DE:329h 10m 51s

Header row: <thead> with <th> directly inside, no <tr> (issue #141)

HostIPv4 addressMAC
router10.0.0.1 AA:BB:CC:DD:EE:FF

Header row: <thead> wrapping a real <tr>, no .table-titles (issue #141)

SSIDChannelSignal
HomeNet36 -52 dBm

The shapes that broke it — one fixture per report

Data table with the reported values

ClientMACEncryption KeyPackage
Yandex-Station-Midi-61QK 48:E1:5C:9F:26:20 WPA2-PSK/CCMP qHMcYUJ3RJ5Zr1Qz8xN2vB7kL0pW6sT4dG9fH1jK3mP5nR7tV9xZ2bC4eF6gJ8lN0qS2uW4yA6cE8hK1mO3rT5vX7zB9dG1jL3nQ5sU7wY9aD2fH4kM6pR8tV0x luci-app-attendedsysupgrade

Key/value with an unbreakable value (issue #36)

ENTRYPOINT
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Driver
overlay2

No header row — a log, which scrolls rather than cards

2026-08-14 20:43:11
daemon.notice
netifd
Interface wan is now up, protocol dhcp, device eth0.2, metric 0
2026-08-14 20:43:12
daemon.warn
dnsmasq
reducing DNS packet size for nameserver 2001:db8:1a2b:3c4d:5e6f:7a8b:9c0d:1e2f to 1280

The forcing utilities (.left/.right/.center + .top/.middle/.bottom)

leftcenterright topmiddlebottom
left center right top
two lines
middle
two lines
bottom
two lines

Key/value table, narrow container (System -> System at 390px)

The @container fs-content query below only fires inside a container narrower than 560px. `.g-wrap` (this page's only other fs-content container) never gets that narrow, so without a fixture of its own this rule is unrendered and computed-diff reads 0 for it. The 340px `.cbi-map` here stands in for the sidebar layout's cramped content column.

System

Firmware Version
OpenWrt 24.10.5 r28134-cb8087f118
Kernel Version
6.6.104
Architecture
ARMv8 Processor rev 4

CBI section table — firewall zones, port forwards

Zones

Zone Input  
lan accept
wan reject

CBI section table — header reflow at the card boundary (RU, task 0163)

`.cbi-map` pinned to 968px — firewall/zones' own measured content column at 1024px, live, RU. The card fires on THIS element (theme/45-misc.css), one level above `.cbi-section`'s own `--fs-card-pad`, so 968px here only hands the table 936px. The six real header strings below need ~1017px at the word floor (base/40-tables.css) — before the 992px fix (theme/65-dropdown.css) this table stayed a table at 968px and clipped its own header row with no scroller in reach; 968 now clears 992 and the table cards instead.

Зоны

Зона ⇒ Перенаправления Входящий трафик Исходящий трафик Внутризональная пересылка Маскарадинг IPv4  
lan принимать принимать да нет

Row actions, plain table (Startup-style, no .cbi-section-table)

dnsmasq

Pager

Page 2 of 7

The chrome bar (#indicators, .fs-themerow, .fs-logout)

Refreshing Unsaved: 3
Log out

Modal

Overview card header — disclosure control (pages/20-overview.css)

System

Hostname
OpenWrt
Model
Generic