Information blocks
The vocabulary every Yuno detail screen is read from. form-field is how a screen ASKS for data; this is how it SHOWS it. Payment details, a reconciliation run, an API key, a seller, an audit event: anything the Dashboard reflects back from an API is read through these pieces, which is why they are not a payments component.
Anatomy
A field is a label over or beside a value. The label is semibold foreground and the value is muted — inverted from a form, and the single easiest thing to get backwards. In a form the label is quiet and what you typed is the ink; here you are scanning for the field you want among thirty, not reading prose.
- 1Label
14/20 semibold foreground. The strong half.
- 2Copy
Optional, beside the label. On a sensitive field it MOVES into the action cluster.
- 3Value
14/20 regular muted. A string, or anything you hand it as children: a StatusBadge, a CountryCell, a payment method, a row of chips.
The four shapes
A list has four shapes and each answers a different question about the DATA, not about the width. Horizontal puts the label beside the value and lines every value up on one edge, which is what keeps thirty fields scannable — it is the default a detail screen wants. Vertical stacks the label over the value, for values too long or too unlike to share a column. Drawer is a Sheet, where the value goes hard against the right edge and never wraps. Separated puts a rule between every field, for a panel whose values have nothing in common.
{/* Horizontal — the default a detail screen wants. */}
<InfoList>
<InfoField label="Payment ID" value={id} copyable />
<InfoField label="Status"><StatusBadge status="succeeded" /></InfoField>
</InfoList>
{/* Vertical — the label stacked over its value. */}
<InfoList orientation="vertical">…</InfoList>
{/* Separated — a rule between fields whose values are unlike. */}
<InfoList separated>…</InfoList>Values a role gates
Some data cannot be shown to everyone. It travels masked and only a specific role reveals it — and a sensitive field is a DIFFERENT COMPOSITION, not the same field with an eye added: the copy leaves the label and joins the eye in a cluster on the right, with a rule between them. The gate is real, not decorative. Masked, the copy is disabled, because copying a secret you are not allowed to read hands it over anyway. Revealed, the glyph opens and the copy lifts to foreground. With no role there is no cluster at all — an eye that cannot reveal and a copy that cannot copy are two dead controls, and someone without the role always sees the masked value. The mask itself is yours: which clue survives is a decision per field.
{/* The mask is yours — which clue survives is a decision per field. */}
<InfoField
label="Tax ID"
value={taxId}
masked="•••••6789"
canReveal={user.can("pii.read")}
/>
{/* No role: always masked, and no cluster at all. */}
<InfoField label="Phone" value={phone} masked="+1 ••• ••• 4532" />In a Sheet
A Sheet is its own set in the Figma, not a narrower page. There are exactly two shapes and which one you get follows from the column count: one column is a row per field with the value against the right edge, for few fields or long values; two columns stack the label over its value, for many short fields where one row each would make a very long panel. Either way the value stays on ONE line and truncates, and the whole of it comes back in a Tooltip. And the fields are never bare against the panel — they live in the same InfoBlock card as on a page.
<SheetBody>
<InfoBlock title="General">
<InfoList drawer columns={1}>
<InfoField label="Entity ID" value={entityId} copyable />
</InfoList>
</InfoBlock>
</SheetBody>The card
InfoBlock is the card a group of fields sits in, and it is not optional chrome. The same card serves a page and a Sheet; inside a Sheet the body supplies its own padding around it. Its heading takes the kit's Title atom.
Seller detail
<InfoBlock title="Seller detail">
<InfoList separated>
<InfoField label="Key name" value="restricted_prueba" />
<InfoField label="Member with access">
<div className="flex flex-wrap gap-2">
<Badge variant="secondary">Margarita Rodriguez</Badge>
</div>
</InfoField>
</InfoList>
</InfoBlock>One card, several groups
A block can hold more than one named run of fields — General, then Payment method, then Customer — and it can fold. Each section carries its own leading rule rather than the block spacing them out: the Figma puts one above every section including the first, so the header is divided from its content by the same device, and a section that owns its rule can be dropped anywhere without the caller counting which one is first. It opens OPEN, and should stay that way: the Figma draws a CaretUp, and a caret pointing up means fold me, not unfold me. A detail block that opens folded hides what the reader came for. The whole header is the control, because a 16px glyph is a small target and a header carrying a caret already reads as one.
General
Payment method
<InfoBlock title="Seller detail" collapsible>
<InfoSection title="General">
<InfoList>…</InfoList>
</InfoSection>
<InfoSection title="Payment method">
<InfoList>…</InfoList>
</InfoSection>
</InfoBlock>The top of a detail screen
Three tiers, and the order is the argument: the number you came for, the handful of facts that qualify it, then the identifiers you need once you have decided to care. A detail screen that opens with a grid of IDs makes you hunt for the amount. The summary strip is separated by vertical rules and the identifiers by a horizontal one — the same device at two scales, which is what keeps a block this dense from reading as a wall. The identifiers FLOW: as many per line as fit, wrapping onto the next.
$52.000,00 COP
<PrincipalBlock
headline="$52.000,00"
headlineSuffix="COP"
actions={<Button variant="outline" size="sm">Download</Button>}
summary={[
<InfoField key="c" label="Captured" value="$22.000,00 COP" />,
<InfoField key="s" label="Status"><StatusBadge status="succeeded" /></InfoField>,
]}
details={[
<InfoField key="p" label="Payment ID" value={id} copyable />,
]}
/>Import
Copy this import at the top of the file where you compose these.
import {
InfoField,
InfoList,
InfoBlock,
PrincipalBlock,
} from "@/components/patterns/info-block";