Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# blockr.ui (development version)

* The warning border, `--blockr-color-border-warning`, is amber-600
(`#d97706`) in the light scheme, a new step of the amber ramp, up from
amber-500: a warning border, and a status dot drawn in one, now clears 3:1
on white (WCAG 1.4.11). A test holds icons and status borders to 3:1 on the
surface in both schemes.

* Escape and a click outside go through one dismiss stack, `Blockr.layer()`
(#49). Every control that opens something registers it as a layer: the
Select's list and its expanded tags, the code field's completions, both
Expand Down
3 changes: 2 additions & 1 deletion inst/assets/css/blockr-tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
--blockr-amber-50: #fffbeb;
--blockr-amber-100: #fef3c7;
--blockr-amber-500: #f59e0b;
--blockr-amber-600: #d97706;
--blockr-amber-700: #b45309;

--blockr-green-50: #f0fdf4;
Expand Down Expand Up @@ -115,7 +116,7 @@
--blockr-color-border-strong: var(--blockr-grey-300);
--blockr-color-border-accent: var(--blockr-accent-600);
--blockr-color-border-danger: var(--blockr-red-600);
--blockr-color-border-warning: var(--blockr-amber-500);
--blockr-color-border-warning: var(--blockr-amber-600);
--blockr-color-border-success: var(--blockr-green-600);

/* ------------------------------------------------------------ tints */
Expand Down
8 changes: 5 additions & 3 deletions tests/testthat/_snaps/tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
--blockr-amber-50: #fffbeb
--blockr-amber-100: #fef3c7
--blockr-amber-500: #f59e0b
--blockr-amber-600: #d97706
--blockr-amber-700: #b45309
--blockr-green-50: #f0fdf4
--blockr-green-100: #dcfce7
Expand Down Expand Up @@ -61,13 +62,13 @@
--blockr-color-border-strong: #d1d5db
--blockr-color-border-accent: #2563eb
--blockr-color-border-danger: #dc2626
--blockr-color-border-warning: #f59e0b
--blockr-color-border-warning: #d97706
--blockr-color-border-success: #16a34a
--blockr-color-bg-accent-subtle: color-mix(in srgb, #2563eb 7%, transparent)
--blockr-color-bg-accent-subtle-hover: color-mix(in srgb, #2563eb 13%, transparent)
--blockr-color-border-accent-subtle: color-mix(in srgb, #2563eb 35%, transparent)
--blockr-color-border-danger-subtle: color-mix(in srgb, #dc2626 35%, transparent)
--blockr-color-border-warning-subtle: color-mix(in srgb, #f59e0b 45%, transparent)
--blockr-color-border-warning-subtle: color-mix(in srgb, #d97706 45%, transparent)
--blockr-color-border-success-subtle: color-mix(in srgb, #16a34a 35%, transparent)
--blockr-focus-ring: 0 0 0 3px rgba(37, 99, 235, 0.12)
--blockr-focus-outline: 2px solid #2563eb
Expand Down Expand Up @@ -116,7 +117,7 @@
--blockr-color-error: #dc2626
--blockr-color-danger: #dc2626
--blockr-color-success: #16a34a
--blockr-color-warning: #f59e0b
--blockr-color-warning: #d97706
--blockr-color-warning-bg: #fffbeb
--blockr-color-warning-text: #b45309
--blockr-font-size-section: 1rem
Expand Down Expand Up @@ -144,6 +145,7 @@
--blockr-amber-50: #fffbeb
--blockr-amber-100: #fef3c7
--blockr-amber-500: #f59e0b
--blockr-amber-600: #d97706
--blockr-amber-700: #b45309
--blockr-green-50: #f0fdf4
--blockr-green-100: #dcfce7
Expand Down
24 changes: 24 additions & 0 deletions tests/testthat/helper-tokens.R
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,30 @@ expand_hex <- function(hex) {
tolower(hex)
}

# WCAG 2's contrast ratio of two opaque hex colours.
contrast_ratio <- function(colour, background) {

luminance <- blockr.core::dbl_ply(c(colour, background), relative_luminance)

(max(luminance) + 0.05) / (min(luminance) + 0.05)
}

relative_luminance <- function(hex) {

channel <- strtoi(
substring(expand_hex(hex), c(2L, 4L, 6L), c(3L, 5L, 7L)),
16L
) / 255

linear <- ifelse(
channel <= 0.04045,
channel / 12.92,
((channel + 0.055) / 1.055)^2.4
)

sum(c(0.2126, 0.7152, 0.0722) * linear)
}

# The selectors a stylesheet styles, one per comma-separated part, with the
# rules inside an at-rule read like any other.
css_selectors <- function(file) {
Expand Down
30 changes: 30 additions & 0 deletions tests/testthat/test-contrast.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,33 @@ test_that("text on the accent tint is text-accent-strong", {
character()
)
})

test_that("icons and status borders clear 3:1 on the surface", {

# What marks a control or its state (an icon, a status border, the status
# dot drawn in one) needs 3:1 against the surface (WCAG 1.4.11), which the
# Chrome check's axe does not cover. The disabled colour is exempt, as it is
# for text.
marks <- paste0(
"--blockr-color-",
c(
"text-muted", "text-default", "border-accent",
"border-danger", "border-warning", "border-success"
)
)

for (scheme in c("light", "dark")) {

tokens <- blockr_tokens(scheme)
ratios <- blockr.core::dbl_ply(
tokens[marks],
contrast_ratio,
tokens[["--blockr-color-bg-surface"]]
)

expect_identical(
sprintf("%s %s: %.2f:1", scheme, marks, ratios)[ratios < 3],
character()
)
}
})
48 changes: 28 additions & 20 deletions vignettes/articles/design-system.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ because a colour chosen for text is not automatically right as a fill.
| grey | 50 100 200 300 400 500 600 700 800 900 | `#f9fafb` `#f3f4f6` `#e5e7eb` `#d1d5db` `#9ca3af` `#6b7280` `#4b5563` `#374151` `#1f2937` `#111827` |
| blue | 50 100 500 600 700 | `#eff6ff` `#dbeafe` `#3b82f6` `#2563eb` `#1d4ed8` |
| red | 50 100 600 700 | `#fef2f2` `#fee2e2` `#dc2626` `#b91c1c` |
| amber | 50 100 500 700 | `#fffbeb` `#fef3c7` `#f59e0b` `#b45309` |
| amber | 50 100 500 600 700 | `#fffbeb` `#fef3c7` `#f59e0b` `#d97706` `#b45309` |
| green | 50 100 600 700 | `#f0fdf4` `#dcfce7` `#16a34a` `#15803d` |
| accent | 50 100 500 600 700 | points at blue |

Expand Down Expand Up @@ -131,7 +131,7 @@ lands on grey-100.
| `--blockr-color-border-default` | grey-200 | every border, input borders included |
| `--blockr-color-border-strong` | grey-300 | hovered borders, stronger lines, a border drawn with a text colour |
| `--blockr-color-border-accent` | accent-600 | focus, full-strength accent edge |
| `--blockr-color-border-danger` / `-warning` / `-success` | red-600 / amber-500 / green-600 | status edges |
| `--blockr-color-border-danger` / `-warning` / `-success` | red-600 / amber-600 / green-600 | status edges |
| `--blockr-color-border-accent-subtle` | `color-mix(in srgb, var(--blockr-color-border-accent) 35%, transparent)` | the accent tint's edge |
| `--blockr-color-border-danger-subtle` / `-success-subtle` | the status border at 35% | the edge of a status badge, a message, the destructive button |
| `--blockr-color-border-warning-subtle` | the warning border at 45% | the same; amber is paler, so its edge is stronger |
Expand All @@ -151,7 +151,11 @@ without the edge). Hovering a selected thing keeps its look.

Every status role has a text, a background and a border token: danger,
warning, success. `error` is `danger`; there is one red. Warning's border is
amber-500, `#f59e0b`. Blue is never a status colour, because blue means "on".
amber-600, `#d97706`: a status border, and a status dot drawn in one, marks a
state, so it clears 3:1 on white (WCAG 1.4.11), where amber-500 is 2.2:1.
Amber-700, the warning text, would clear it too, but it is as dark as the red,
and an amber dot and a red one would then differ by hue alone. Blue is never a
status colour, because blue means "on".
Negative numbers in a table are data, and their colour belongs to
blockr.theme.

Expand Down Expand Up @@ -631,7 +635,7 @@ selected row, the active group's front tab.
padding, 14px `text-default`. Placeholder `text-muted`.
- Focus: `border-accent` and `--blockr-focus-ring`, on any focus.
- Disabled: `text-disabled`.
- Required and empty: `border-warning` (amber-500, `#f59e0b`), `bg-warning`
- Required and empty: `border-warning` (amber-600, `#d97706`), `bg-warning`
fill, and a `*` after the label. The cue clears when the field has a value.
- Commit on Enter or blur. The ↵ button (24px square, the accent tint) is
armed while the field is dirty. Escape reverts. See [Keyboard
Expand Down Expand Up @@ -837,9 +841,11 @@ icon-only button has a tooltip. Tools do not shrink with the panel.

The dock header has the "…" menu, and next to it a block's own control where
the block has one (the AI assistant's sparkle, at every width, until a global
assistant replaces it). They are quieter than other tools: at rest their icon
is `border-strong` grey, on hover `text-disabled` on the `bg-hover` wash, so
the header reads as the block's name. The preview's eye stays in the header
assistant replaces it). They look like the tools above: the title's size and
weight make the header read as the block's name, and a quieter icon would
fall under the 3:1 an active control needs (WCAG 1.4.11), as `text-disabled`
(2.5:1) and `border-strong` (1.5:1) do on white.
The preview's eye stays in the header
too, left of the block's own control: while building, the preview is what
gets switched most. Controls is the menu's first row, with its familiar icon
and a check when on, then a rule, then the block's actions. The header has one size at every
Expand Down Expand Up @@ -1212,20 +1218,21 @@ Specified under "A pill that opens a menu" in [Controls](#controls).
### Block status

An 8px dot on the block's mark with a 2px `bg-surface` ring, one spec for dock
and the DAG (`block_status_style()`). Its fills are local tokens owned by
blockr.dock, each pointing at a meaning token:
and the DAG (`block_status_badge()`). Its fills are meaning tokens, with no
status tokens of their own: the spec names each token next to its light
value, which a renderer that cannot read CSS (the DAG's canvas) draws with.

| Token | Points at | State |
| State | Fill | When |
|---|---|---|
| `--blockr-dock-status-stale` | `text-muted` | stale |
| `--blockr-dock-status-waiting` | `border-warning`, drawn as a hollow ring (1.5px, `bg-surface` inside) | an input is not ready: nothing linked in, or the block upstream is unset, waiting or failed |
| `--blockr-dock-status-failed` | `border-danger` | failed |
| `--blockr-dock-status-unset` | `border-warning` | a required input is empty; the same amber as the empty field's cue |
| stale | `text-muted` | the inputs changed since the block last ran |
| waiting | `border-warning`, drawn as a hollow ring (1.5px, `bg-surface` inside) | an input is not ready: nothing linked in, or the block upstream is unset, waiting or failed |
| failed | `border-danger` | the block failed |
| unset | `border-warning` | a required input is empty; the same amber as the empty field's cue |

Unset and waiting share the amber and differ by shape: the block that needs
you has the solid dot, and the blocks below it, which wait for it, have rings.
`-unset` replaces the off-palette `#eab308` in blockr.dock
`block_status_style()`.
The amber replaces the off-palette `#eab308` blockr.dock's
`block_status_badge()` gave unset.

### Status line and plain text

Expand Down Expand Up @@ -1554,13 +1561,14 @@ rules (today declared twice, in blockr.viz `chart.css` and blockr.dm
`crossfilter-block.css`), the gear button, the tool button and the action
menu (`tool_button()`, `action_menu()`), the button classes
(`.blockr-btn`), the chevron (`Blockr.icons.chevron`), and the column-label reader. blockr.dock owns the dock
header, the "…" menu, the block status dot, the category colours
(`blk_color()`), the status dot's `--blockr-dock-status-*` tokens and the
rule that hides the gear in simplified mode. blockr.theme owns data colours.
header, the "…" menu, the block status dot and its spec
(`block_status_badge()`, which the DAG draws from too), the category colours
(`blk_color()`) and the rule that hides the gear in simplified mode. blockr.theme owns data colours.

The token tests in blockr.ui check that the vocabulary changes only
deliberately, that every name follows the grammar or is a legacy alias, that
the controls read meaning tokens only, and that consumers' fallbacks agree with
the controls read meaning tokens only, that icons and status borders clear
3:1 on the surface in both schemes, and that consumers' fallbacks agree with
the tokens. Palette and legacy reads elsewhere in blockr.ui change only
deliberately too. To do: warn on every palette or legacy read in the packages
that consume the tokens.
Expand Down
Loading