Skip to content

fix(ios): give a filled button a visible edge when its tint matches the surface - #78

Open
CodyPChristian wants to merge 1 commit into
NativePHP:mainfrom
CodyPChristian:fix/ios-button-surface-collision
Open

fix(ios): give a filled button a visible edge when its tint matches the surface#78
CodyPChristian wants to merge 1 commit into
NativePHP:mainfrom
CodyPChristian:fix/ios-button-surface-collision

Conversation

@CodyPChristian

Copy link
Copy Markdown
Contributor

Problem

A filled button whose tint resolves to the same colour as the surface it sits on has no visible edge — it reads as text, not a control.

This is easy to hit with a themed palette. A site whose brand colour is a pale cream and whose card surface is that same cream renders its primary action invisible:

primary: #F4F1EC   surface: #F4F1EC   →  "Sign in" vanishes into its card

Fix

When primary or secondary collides with the surface, substitute a token that contrasts and keep the button filled. Filled matters — outlining preserves the brand colour but reads as a secondary control, which is wrong for a screen's main action.

The two-parent guard

The renderer cannot see what it is actually sitting on. NodeView, which paints container backgrounds, lives in the host package, so no ambient-surface value reaches the button — and the same button may be used on a card (surface) or directly on the page (background).

So a candidate is only accepted when it contrasts with both. Without that guard, a palette whose secondary equals its page background gets fixed on the card and goes invisible on the canvas:

secondary #314E5E  vs surface #F4F1EC  ✓ contrasts
secondary #314E5E  vs background #314E5E  ✗ identical  → rejected
on-surface #0F172A vs both  ✓  → accepted

on-surface is tried next and contrasts with the surface by construction.

Scope

  • Semantic variants (destructive, success, accent) are never substituted — a slate Delete button would not read as destructive. They fall back to a hairline outline in their own label colour, as does any variant with no qualifying candidate.
  • Detection is a channel-delta threshold (~0.12) tuned to catch "same colour to the eye" without firing on merely light-on-light, so a normal palette is untouched.

Verified against four production white-label palettes: only the colliding one changes behaviour, the other three resolve identically to before.

…he surface

A filled button whose tint resolves to the same colour as the surface it
sits on has no visible edge -- it reads as text, not a control. This is
easy to hit with a themed palette: a site whose brand colour is a pale
cream and whose card surface is that same cream renders its primary
action invisible.

When primary or secondary collides with the surface, substitute a token
that contrasts and keep the button filled. Filled matters: outlining it
would preserve the brand colour but read as a secondary control, which
is wrong for a screen's main action.

The renderer cannot see what it is actually sitting on -- NodeView, which
paints container backgrounds, is in the host package, so no ambient
surface value reaches the button, and the same button may be used on a
card or directly on the page. A candidate is therefore only accepted when
it contrasts with both surface and background. Without that guard a
palette whose secondary equals its page background goes invisible on the
canvas while the card gets fixed. on-surface is tried next; it contrasts
with the surface by construction.

Semantic variants (destructive, success, accent) are never substituted --
a slate Delete button would not read as destructive -- and fall back to a
hairline outline in their own label colour, as does any variant with no
qualifying candidate. Detection is a channel-delta threshold tuned to
catch 'same colour to the eye' without firing on light-on-light, so a
normal palette is untouched.
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.

1 participant