Skip to content

Checks ​

npm run audit checks the components against the rules of kata. It has two parts: a reading of the sources, and a measurement of the examples in a browser. npm run check runs it with the other checks of the repository (CONTRIBUTING lists them).

The sources ​

scripts/lint-components.mjs reads the styles of the components in src/svelte.

  • Spacing, type and colour take a token, never a raw length or colour.
  • padding takes the pad scale and gap the gap scale.
  • Only the components that hold text in a control (Button, Toggle, TextInput and the other controls, a mark such as Badge, a list item, a pair or a table cell), at a container's edge (Text) or next to a line (Section, SectionHeader) trim text. The seat of a mark trims an empty line, and so does the summary of Prose, which seats its chevron.
  • No negative distance, no outer margin on a component's root (the layouts, Icon and Prose aside), no @media for a width and no :has() other than the next sibling. A pseudo-element is moved by a negative translate only inside the reach mixin, which centres the hit area of a control without a line or a surface on it; an element centred on a point and an animation are not counted.
  • Every custom property a component reads is defined.
  • Every component is exported, has an example and has a page here or in the Workbench chapter.

The examples ​

The audit builds the examples and opens each one in Chromium, served by Vite's preview server. Every example is measured at three widths (1440, 768 and 390px), at the default and the largest text size, in the dark and the light theme, and with an English and a Japanese root. Every visible element inside the example is measured; a finding fails the audit.

RuleWhat is measured
spacePadding and gaps are 0 or a step of the scale
marginOuter margins appear only in Prose and Block, never negative
typeFont sizes are type roles; line heights are a step of the scale
borderBorders are 0, 1 or 2px and solid, so no border of the browser
heightAn element with data-h has that height (a reach: its parent's)
trimText is trimmed in a control, at a container's edge or at a line
trim-clipTrimmed text keeps its vertical overflow, so no ink is cut
cursorWhat can be pressed shows the pointer
contrastText reaches 7:1 on its surface (4.5:1 when disabled or dimmed)
focus-haloA text field shows a 2px ring on focus
double-ruleNo two lines run along one edge
double-insetNo padded container or padded item sits in a padded container
bundle-edgeText in an unpadded container or surface keeps pad-md
inner-gapIn a padded container, neighbours are no further than the edge
box-touchA control never touches the padded edge of its container
box-gapControls stacked vertically are at least md apart
rule-gapA line is as far from what is above as from what is below
head-gapA section header's head is pad-md from its content
head-nearA group's head is nearer its content than the group above
page-head-gapA page's head is pad-lg from its content
section-head-gapA section's head is gap-lg from its content
tabs-gapContent under Tabs is gap-lg away (not in an unpadded container)
read-rowA list item that is only read has a line or a surface
first-lineA mark in a seat is centred on the ink of the first line
nearA control's description is nearer its label than the next one's
edgeThe first and last things are pad from a padded container's edge
overlapThe children of a layout do not overlap
crushText is never squeezed narrower than two characters
fixed-frameA Shell or an embedded root is not the frame of fixed elements
scroll-markA region that overflows shows a size-sm scrollbar on that axis
thumbThe thumb of that scrollbar reaches 3:1 on the region's surface

Distances are measured from the edge of what is visible: the outline of a component with a line or a surface, a line, the inner edge of a container, the ink of text, the square of an icon (not one that hangs from a seat of no height), the box of a cell of a set that shows which one is chosen (aria-pressed, role="radio", or aria-current on the square of an icon button), or the outside of the scrollbar of a region that scrolls, on the side the scrollbar runs along. A control without a line or a surface is measured as what it shows; its reach takes no room. A line along one side of a component only is an edge on that side only; from the other side the distance runs to what is inside it (the text of tabs with a line along their bottom). An element marked data-kata-skip (drawn by the browser or by another library) is not measured.

head-gap allows two exceptions. Flush content under a head with an action keeps pad-md above it, so that the action that hangs below the head does not reach the first row, and its distance from the head may grow by that padding and the action's overhang (its box, or the reach of a control without a line or a surface). Flush content without an action keeps gap-xs above it, and its distance may grow by that gap. head-near allows the first group to lie further from its head by the action's overhang, and the second by the gap-xs.

first-line measures every seat (a mark beside text): an element whose ::before is an empty line trimmed to its ink. The centre of the mark it holds is within 0.06px of the centre of the ink of the first line of the text beside it (the nearest sibling after it that holds text, else before it). The ink runs from the cap height to the baseline of the font, measured with a trimmed line placed before the text, and with a Japanese root it also takes the CJK ink above and below them, as a trimmed line does. The chevron of a Prose summary is drawn by the summary's own ::before and is not measured, and neither is the end of a Text that has moved to a line of its own under the text (data-under).

scroll-mark measures every element that overflows on an axis with overflow auto or scroll (Regions that scroll): the box less its borders and its client size on that axis is the track's thickness, within 1.5px, as the client size is whole pixels and the scrollbar snaps to them. A hidden scrollbar measures 0. thumb composes thumb over the surface of the region and its ancestors, as contrast does for text. The audit runs Chromium without Playwright's hidden scrollbars, and hides only the page's own scrollbar, so that the three widths are the content's. The embed example puts kata under a root (data-kata-root) in a page that sets its own scrollbar colors, without the base CSS; the audit reads the tokens from the root of what it measures, so every check, scroll-mark included, measures the embed as it measures a page.

near measures a control with a description (a radio, a checkbox or a switch, which marks itself data-control) that another control follows: the distance from the ink of its label to the ink of its description is less than the distance from the ink of its last line to the ink of the next control's label.

edge measures every container with padding: the first visible thing in it is its padding below its inner top edge, and the last one its padding above its inner bottom edge, within 0.06px. A Grid gives the cells of a row one height, so a container in a cell may end below its content: the bottom is measured only where the box of its last child reaches the padding. A container that scrolls is measured at the ends of what it scrolls.

height measures a control without a line or a surface (the reach mixin) by its reach, the ::before that carries its hit area, hover surface and focus ring, against the reach its parent gives it (--kata-reach, a small button's by default) rather than its data-h. double-inset also measures an item that keeps its own padding above and below (a list item, a comment): no container with padding holds it without a line or a surface between them. The edge of such an item is an edge for trim too: the last line of the body of a comment is trimmed at its padding.

Released under the Apache-2.0 license.