Skip to content

Design system 1/3: blockr.ui's tokens, dark mode, card layout - #482

Merged
nbenn merged 26 commits into
mainfrom
feat/ds-1-tokens
Sep 30, 2026
Merged

nbenn merged 26 commits into
mainfrom
feat/ds-1-tokens

Conversation

@christophsax

@christophsax christophsax commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Not ready to merge: this needs blockr.ui #46 and #47 merged first. Until then DESCRIPTION installs blockr.ui from feat/design-system-2; that Remotes line goes once #47 is on main.

First of three stacked PRs that move the dock onto blockr.ui's design system. The next two are #483 (menus and the options sidebar) and #484 (simplified mode).

The dock reads its colours, radii, surfaces and type from blockr.ui's tokens, so a board follows blockr.ui's dark scheme. dock_board_options() gets a light/dark switch, light by default. The block header drops its subtitle: a mark in the block's category colour carries the status dot, and the block type with its package is the mark's tooltip. The dock's chrome uses blockr.ui's tooltip instead of the native one.

  • Dock reads blockr.ui's design tokens
  • Light/dark switch in the dock board options
  • Block header, rename and tooltips from the design system
  • Card layout: controls closer to the header, one rule above the preview
  • Sidebar header on the navbar's line, thin pin and close
  • NEWS and version (0.1.3.9001)

The simplified mode in #484 is a requirement still being worked out: #489.

The block header, corner radii, the sidebar chrome, the dockview shell and the remaining dock colours read blockr.ui's tokens instead of fixed values, so a theme restyles the dock.
The board options offer blockr.core's dark-mode switch; the tokens carry the dark scheme.
The block header and its rename field follow the design system, toggles that are on get the lighter accent, the subtitle goes, and the dock's chrome takes its tooltips (the light card) from blockr.ui's Blockr.tooltip.
A thin x on the tab close button, the controls moved up under the header, a single rule between controls and preview, and a 42px block mark.
@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.08257% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
R/action-ui.R 93.33% 1 Missing ⚠️
Files with missing lines Coverage Δ
R/block-meta.R 96.00% <ø> (ø)
R/board-ui.R 100.00% <100.00%> (ø)
R/dock-board.R 88.81% <100.00%> (+0.15%) ⬆️
R/plugin-block.R 99.57% <100.00%> (-0.01%) ⬇️
R/sidebar-server.R 95.72% <100.00%> (-0.40%) ⬇️
R/action-ui.R 80.53% <93.33%> (+0.14%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The feat/design-system-2 branch went with blockr.ui#47, and blockr.ui
keeps its version at 0.0.1 between releases, so the 0.0.1.9041 floor
could no longer be met.
A block without inputs keeps the default "inputs" among its visible
sections, so its card drew the rule above the preview with no controls
over it.
The card refuses a name of spaces like an empty one, but the field
reports every keystroke, and req() only stopped the empty string: the
board took the blank name, and the tab went blank, until the card
restored it on blur.
On a locked board a double-click still opened the field, and the card
showed a name the board had refused. The title now carries the gesture
only when unlocked, and with it data-blockr-editable, which gives it
blockr.ui's text cursor and "Double-click to edit" tooltip; the hover
wash follows the same attribute.
The dot's blockr.ui tokens and the hollow waiting ring lived in
block_status_dot_attrs() alone, which re-derived the status to pick
them: a stale block with errors from its last run drew a red dot
labelled "Inputs changed since this block last ran", while
block_status_badge() and the DAG kept it grey. block_status_style()
now names the tokens and the shape next to the literals, so the dot
paints from the badge and the DAG can draw the same one.
At rest the "..." and a section toggle that is off took border-strong,
which the dark scheme keeps faint for lines: 1.38:1 on the card, where
the icon all but vanished. They now take text-disabled at rest and
text-muted on hover, the pair a regular tool takes one step up.
Core ties thematic to the light/dark switch only when a board carries
both options, so plots stayed white on the dark card. The switch comes
from core and stays off by default: thematic has its own rough edges,
but a user who turns it on gets plots in the board's colours.
The comments still spoke of plain values, a header with no inline
style, and a white-ringed dot at the lower right placed to match the
DAG's.
@nbenn

nbenn commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

I pushed eight commits on top of yours, @christophsax, from reviewing this against blockr.ui main:

  • Install blockr.ui from main now that its design system has merged. The feat/design-system-2 branch went with Design system, part 2: menus, tooltips, table preview, buttons, keyboard hints blockr.ui#47, and blockr.ui keeps its version at 0.0.1 between releases, so blockr.ui (>= 0.0.1.9041) could not be met: test_local() refused to load and R CMD check stopped on "unsuitable version". NEWS loses its "Requires blockr.ui" line.
  • Name only the sections a card has in its data-open. A block without inputs keeps the default "inputs" among its visible sections, so an rbind card drew the preview rule right under its header.
  • Keep a blank block name from reaching the board. The field reports every keystroke and req() only stops "", so typing spaces renamed the block, and blanked its tab, until blur restored the name.
  • Offer the in-place rename only where the board takes it. On a locked board a double-click still opened the field, and the card kept a name the board had refused (the old single click did the same). The title now carries the double-click and data-blockr-editable, which I moved here from Design system 2/3: menus, block header, options sidebar #483, only when unlocked, and the hover wash keys off the attribute. That also gives this PR the spec's text cursor and "Double-click to edit" tooltip on its own.
  • Carry the status dot's tokens and shape in the shared spec. The dot's tokens and the hollow waiting ring lived in block_status_dot_attrs() alone, which re-derived the status to pick them, so a stale block with errors from its last run drew a red dot labelled "Inputs changed since this block last ran" while the DAG kept it grey. The shared block_status_style() now names each fill's meaning token and the shape next to the literals, and the dot paints from the badge. The spec's --blockr-dock-status-* tokens stay undeclared: the dot reads blockr.ui's tokens directly, the spec says so in Bring the warning amber and the dock's header tools to 3:1 blockr.ui#61, and the DAG follows in Draw the DAG from blockr.ui's tokens, status badge included blockr.dag#174.
  • Draw the header tools in text tokens, one step quieter than a tool. The spec's border-strong put the "…" at 1.38:1 on the dark card, where it all but vanished. With text-disabled at rest and text-muted on hover they stay quieter than other tools, at 3.75:1 in dark and 2.54:1 in light. The spec follows in the same blockr.ui PR.
  • Offer the thematic switch among the dock's options. Core only makes thematic follow the light/dark switch when a board carries both, so plots stayed white on the dark card. It stays off by default, since thematic has rough edges of its own: after a light/dark flip a plot keeps the previous scheme's colours until it renders again, on a plain core board too.
  • Describe the block header and status dot as they now are. The comments, and the block_status_badge() docs, which promised that the dock and the DAG render the same colour and styling.

Merging this into #483 conflicts in three places. In block_card_title(), where both add data-blockr-editable, keep this branch's side. In dock_board_options() and its docs, keep both options, the thematic switch from here and the compact one from #483, and regenerate man/dock.Rd. The board-class snapshots then need accepting again, since the board has four options.

One heads-up, with nothing to change here: with controls_dep() on every board, blockr.ui's controls meet blockr.dplyr's copies under the same dependency names, which BristolMyersSquibb/blockr.ui#50 counts. Since blockr.ui is at 0.0.1 and blockr.dplyr at 0.2.0.9011, a board that renders a dplyr block at page load gets dplyr's Select and Input (htmltools keeps the higher version), while a dplyr block first rendered later, in a second view, runs on blockr.ui's (Shiny keeps the one loaded first). A filter block worked both ways. Both blockr-ui.js and dplyr's blockr-core.js also add a document click handler over Blockr._docClick, so every Blockr.onDocClick callback runs twice per click on such boards.

@nbenn

nbenn commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

I also merged main into this branch: BristolMyersSquibb/blockr.core#337 retired the per-block required channel, which #420 adapted the dock to, and without #420 here the e2e apps stopped on attempt to apply non-function, failing 50 tests in the smoke job.

The rule above the preview needed to know whether controls are open,
which a data-open attribute written from R and kept current by
block-sections.js told it. The panels' hidden accordion buttons already
say it, and Bootstrap marks one collapsed as its panel starts to close,
so :has() reads the same state at the same moment, and a card without
controls needs no special case.
bsicons is already imported, and bs_icon("pin-angle") draws the same
path the function carried by hand, character for character.
The four blue aliases named blockr.ui's blue ramp, which a theme that
changes the accent does not touch, so the focus rings, the selected card
and the add button of the block browser, link and stack menus would have
stayed blue. They now name the accent ramp, which points at the same
blues by default.
History (what the markup used to be) and restatements of the rule below
them go, and so does a comment claiming a weight its rule never set. The
ones that explain a constraint stay, shorter. The two .blockr-block-mark
rules become one.
Every card carried its own inline copy of the rename handler, about 40
lines of jQuery in an R string, plus an inline ondblclick. One handler
on the document now serves all cards, whenever they were inserted, and
starts from the double-click either block menu sends. An e2e test covers
refusing an empty name, Escape, and a commit reaching the board.
The header's menu button and section toggles overrode Bootstrap's button
properties with 12 `!important`s. They now set the `--bs-btn-*` variables
Bootstrap's own state rules read. Scoped to the header's actions, the rules
tie blockr.ui's `.btn-light` rule, which sets the same variables, and win
on the source order the board-ui test pins. Hover swaps
the rest pair that every state reads, so an open menu, a focused tool and
a toggle that is on keep their look off the pointer and take the hover
look under it, as before.

Measured on every state (rest, hover, pressed, open, open under the
pointer, keyboard focus; a toggle on and off, each under the pointer, and
focused), the colours, backgrounds, outlines, sizes, padding and radius
match the old rules exactly. The only computed difference is a zero-width
border's style and colour, which draw nothing. The menu button drops its own
focus ring, which repeated blockr.ui's `.btn:focus-visible` rule, and the
transition now comes from blockr.ui's `.btn` rule.
The header's tools rested in text-disabled, 2.5:1 on white: under the
3:1 an active control's icon needs (WCAG 1.4.11), and the colour
blockr.ui keeps for disabled controls. They now take the colours of
blockr.ui's other tools, text-muted at rest and text-default on the
hover wash, as BristolMyersSquibb/blockr.ui#61 now specifies.
@nbenn

nbenn commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Two follow-ups to my earlier comment, @christophsax, both for the 3:1 WCAG asks of icons and state indicators (1.4.11):

  • Header tools now take the colours of blockr.ui's other tools: text-muted, and text-default on hover. The quieter text-disabled from my earlier commit is 2.54:1 on white, and blockr.ui's token table keeps it for disabled controls.
  • The warning amber moves to amber-600 (#d97706) in Bring the warning amber and the dock's header tools to 3:1 blockr.ui#61, since amber-500 is 2.15:1 on white. The unset and waiting dots here follow once that merges. Amber-700, the warning text, would pass too, but it is as dark as the failed red, so the two dots would differ by hue alone.

@nbenn

nbenn commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

This branch conflicts with #491, now in the merge queue, in five files, @christophsax. That PR drops the dormant case from block_status_badge(), because core has not reported dormant since BristolMyersSquibb/blockr.core#364, so the function returns a styling list or NULL and never NA (#485). A test merge of the two heads leaves the function body clean and conflicts only where this branch rewords the same docs and extends the same test:

  • In R/block-meta.R, keep this branch's wording in both hunks and drop the NA clause from each.
  • In R/plugin-block.R, keep this branch's ring sentence in @param status, with the statuses as Draw status badges from a parked block's held outcome #491 has them: "ready and unevaluated carry none; any other value yields no badge".
  • In tests/testthat/test-plugin-block.R, keep the new token assertions, followed by Draw status badges from a parked block's held outcome #491's loop over "ready", "unevaluated", NULL, … in place of the one over "dormant".
  • In NEWS.md, keep both sides.
  • Regenerate man/meta.Rd rather than resolving it by hand.

Outside those hunks nothing refers to dormant after merging main, apart from #491's own NEWS entry.

@nbenn

nbenn commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

We'll work through these ourselves once #491 is in, @christophsax, and ping you if we need anything.

@nbenn

nbenn commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Done on this branch: the unset and waiting literals in block_status_style() are #d97706, matching blockr.ui's border-warning since BristolMyersSquibb/blockr.ui#61, so nothing is needed from you here.

The badge docs keep this branch's wording without the `NA` return, and
the style test keeps the token assertions with #491's `unevaluated` loop.
The status spec's literal for the unset and waiting dots is the light
value of border-warning, which BristolMyersSquibb/blockr.ui#61 moves
from amber-500 to amber-600 (#d97706), the first amber at 3:1 on white.
The DAG's canvas draws the literal, and the token test holds it to the
token, so this goes up once that PR has merged.
Escape and Enter end the edit by blurring the field, and headless Chrome
fires no blur in a tab that lacks the focus, so the test failed whenever
another tab had been brought up after its own: Escape restored the name
but left the field open. Focus emulation makes the tab behave as focused.
With a second tab in front, the test fails without it and passes with it.
@nbenn
nbenn added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 9ffe0df Sep 30, 2026
10 checks passed
@nbenn
nbenn deleted the feat/ds-1-tokens branch September 30, 2026 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants