Repository navigation
1.4.0
Added
Empty— what stands where content would have been, in four slots:icon,title,childrenandaction. It is the other half ofSkeleton: one is the shape of something on its way, the other is the shape of something that is not coming, and a list showing neither has a blank rectangle where its answer should be.- The headline is defaulted rather than required, because the version that says nothing useful is the version that gets shipped.
titlefalls back to thelocale's way of saying that there is nothing here — a newemptynamespace in the message table, translated into all eighteen languages the library already speaks — andtitle={false}drops it for a state that is a glyph and a sentence.icondefaults to an empty tray and takes any node, so an illustration or another set's glyph goes in without a wrapper;actionholds the way out, and several of them wrap together in one row. variantdefaults totexton this component and on no other: an empty state is nearly always already inside a Card, a Table or a panel, and a second rectangle inside the first is one rectangle too many.outlineandsolidare the undyed container sheet, socolorreaches the hairline and the ring and stops — an empty state arriving in the accent colour is making a claim about content that does not exist. The root is arole="status"live region, which is what lets a list that empties under the reader say so;role={undefined}turns that off for a state that is part of the page on arrival.Mockup— a device with a screen you can put anything on.deviceis the one prop with no default and picks the machine: amobile, atablet, or adesktopthat is either amonitoron a stand or alaptopon a base.osdraws that system's own chrome — a menu bar and a floating dock, a centred taskbar, a top bar with a dock down the leading edge, a status bar with a home indicator or three navigation glyphs — andsystemUi={false}takes all of it away.bezelruns fromnone(no hardware at all, just the glass) throughthinandstandardtothick, which is an older device with a forehead and a chin rather than a wider frame;finishisgraphite,silverorwhite;notchis adynamic-island, anotch, apunch-holeornone, defaulting to whatever the device would really have.orientationturns a handheld, and the screen, the bezel and the cut-out turn with it.- The screen is a viewport at the device's real resolution, not a picture scaled down.
sizeis a five-step ladder of genuine resolutions per device — a phone from 320 to 430 CSS pixels wide, a desktop from 1024 to 1920 — andresolutiontakes a{ width, height }pair for anything else. The whole device is then scaled once to whateverwidth/heightcome to on the page, so the same component laid out inside a phone and inside a desktop wraps differently in each. The screen is also a container namedneba-screen, so content can answer to the device with a container query rather than to the window. scrolllets content taller than the screen scroll instead of being clipped;wallpapertakes any CSSbackgroundfor what sits behind it;timeis the clock, and the only text the chrome draws — every menu title, dock icon and tray glyph is an abstract shape, so there is nothing to translate and no other party's marks in the library. Every part of the device isaria-hidden, which leaves a screen reader withchildrenand nothing else.DataTable— a table for a lot of rows.Tabledraws a grid; this one is a place to work, and it takes the same two props to get there:headersanditems. It is compact by default (sm,compact) with its own row ladder a step below the rest of the library,stripedtakestrue,'odd'or'even', and the parity is counted over the whole set so it does not change as the rows are scrolled or sorted.- Only the rows on screen are in the DOM, and
heightis what turns that on. Every row isrowHeighttall — defaulted off thesize/densityladder — so the offset of a row is its index times a constant and nothing has to be measured on a scroll frame; the rows that are left out stand up as two spacer rows, which is what keeps the scrollbar honest. Without aheightor amaxHeightthere is nothing to measure against and every row is rendered, whatevervirtualsays;virtual={false}asks for that on purpose, for a table small enough that find-in-page matters more than the DOM count. Cells truncate rather than wrap, which is the price of the arithmetic. - Selecting is the file manager's, not the form's. A click chooses a row and drops the rest, Ctrl/⌘ adds one, Shift takes the run between, a drag takes the run under the pointer and scrolls when it reaches an edge, and the arrow keys do all three with the same modifiers — plus Home, End, PageUp, PageDown, Space, Ctrl+A, Esc and Enter.
selectionModeisnone,singleormultiple;checkboxesadds a tick column and is deliberately not the default, because a column of checkboxes says the task is choosing and on most tables it is not. With a selection mode the table is agridwith one tab stop andaria-activedescendant, since a virtual row cannot hold the focus. sortablemakes the headings pressable and they cycle ascending → descending → unsorted, because the order the rows arrived in is a state nothing else can get back to.sortMode="multiple"lets a Shift-click add a second key rather than replace the first.resizableputs a handle on each boundary; the first drag freezes every column at the width the browser had given it, so pulling one moves one, and a double-click restores it. Adjacent columns sharing agroupstring merge under one heading in a second header row.paging="pages"swaps the scroll for a footer holding the range, the number of chosen rows, a page-sizeSelectand aPagination;footershows that bar on its own, so a scrolling table can have the count without the pages.searchmatches every column that has not setsearchable: false, case- and accent-insensitively, andsearchabledraws the field withtoolbarfilling the rest of its bar.manualhands any of'sort','filter'and'pages'back to the caller — withrowCountfor the total — so a table whose rows come from a server is the same component rather than a second one.- The i18n table gains a
tablenamespace — the search field's placeholder, the two ticks' labels, the page-size label, and the footer's count and selection sentences — in all eighteen languages. The two that carry numbers are whole templates with{start},{end},{total}and{count}in them rather than fragments, because what differs between languages is the order the numbers appear in.
Fixed
- Forcing a theme on a nested element now works in both directions.
.dark/[data-theme='dark']on an element that is not the document root was already documented, but the light values were declared on:rootalone, so.light/[data-theme='light']inside a dark page had nothing to switch back to. The light block now carries all three selectors. --neba-plate-solid,--neba-plate-glassand the--neba-shadow-1…--neba-shadow-4ladder moved into the derived block. Each is a base token spread into a shadow list, and declared only on:rootthey froze to the light hairline and the light ambient inside any theme root that was not the document root.
Documentation
- Every live preview carries a theme switch in its top corner, so a component can be read in the theme the page is not in without taking the whole site with it. Untouched previews still follow the site switch, and a preview flipped back to the page's own theme rejoins it.
- An
Emptypage in both locales with eight examples, its props rows, its demos, a card in the component gallery, a place on the sample screen beside the placeholder it is the other half of, and its entry inllms.txt. - A
Mockuppage in both locales with eleven examples, its props rows, its demos, a card in the component gallery, a place on the sample screen and its entry inllms.txt. - A
DataTablepage in both locales with seven examples, two props tables, its demos, a card in the component gallery, twelve thousand rows on the sample screen and its entry inllms.txt. - The nav bar and the sidebar no longer hang off the edges between 1440px and 1600px. The site raises
--vp-layout-max-widthto 1600px, and the default theme centres the shell on the article by taking half of what the page has left over — an expression it writes out five times without a floor, because at its own 1440px it cannot come out below zero. At ours it can: the social icons sat 8px outside the viewport and the sidebar climbed over the article's left margin. All five now read one floored--neba-gutter, which reduces every one of them to the gutter plus nothing, the page margin, or the sidebar's width. npm run docs:devstarts in about four seconds instead of fourteen. All threedocs:*scripts rannpm run buildfirst — ten seconds offormat:fix,tscand terser that nothing then looked at, since the docs resolvenebatosrc/index.tsand importsrc/styles.cssdirectly. Prettier now runs where it always did as part ofnpm run build, and CI still checks it.
Every release is in the changelog on the docs site and in CHANGELOG.md at this tag.