Skip to content

feat(app): glassmorphism as a 5-step transparency level - #51

Merged
simota merged 2 commits into
mainfrom
feat/glassmorphism-levels
Jul 27, 2026
Merged

feat(app): glassmorphism as a 5-step transparency level#51
simota merged 2 commits into
mainfrom
feat/glassmorphism-levels

Conversation

@simota

@simota simota commented Jul 27, 2026

Copy link
Copy Markdown
Owner

glassmorphism was an on/off flag with one fixed look, and the look itself was the only thing anyone wanted to change about it. This turns the key into a five-step level — off, 15, higher meaning more transparent — so the glass can be dialed rather than only switched.

What changes

StartupConfig::glassmorphism becomes a GlassLevel enum, and the two scalar glass constants become level-keyed functions. Per level, window background-opacity / chrome alphas (backdrop · surface · pill):

level opacity backdrop surface pill overlay rim mix
1 0.50 0.18 0.16 0.34 0.68 0.70
2 0.35 0.12 0.10 0.26 0.58 0.80
3 0.20 0.07 0.06 0.18 0.48 0.88
4 0.12 0.040 0.034 0.125 0.40 0.94
5 0.06 0.020 0.014 0.085 0.32 1.00

Level 1 is byte-identical to what glassmorphism = true has always resolved to, so no existing config changes appearance. background-blur-radius stays pinned at the key's maximum (64) at every on-level.

As the faces thin out they stop drawing their own edges, so the rim inherits that job — the rim mix climbs to 1.00 at level 5, where the border is the foreground color. That is the ceiling in the literal sense, and it is why the ladder stops at five. The top two levels are panes you read through rather than on; the faces stay deliberately above zero, because at 0.0 the sidebar stops being a plane and reads as text floating on the desktop, which is a different thing from more transparency.

glassmorphism = true / false still parse, to 1 / off. The written spelling does change: the Settings panel and noa --config now emit off/15, so saving or reverting from the panel rewrites a hand-written glassmorphism = true as glassmorphism = 1.

Review angles

  • chrome.rs and noa-config/src/lib.rs alpha tables — the five ladders are the substance of the change; everything else follows from them.
  • theme_settings/state.rs restore-point machinery — each level owns a different background-opacity/background-blur-radius pair, so the panel re-snaps the two rows it manages on every step while keeping the restore point at the user's own pre-glass values. That interaction is the least obvious part of the diff.
  • GlassLevel::ALL / ON_LEVELS — added because the panel's cycle and four tests enumerated levels as slices, which (unlike a match) keep compiling when a variant is added and silently drop it. Level 4 was unreachable from the panel for exactly one build because of this.

Verification

cargo fmt --all -- --check, cargo clippy --workspace --all-targets (no warnings), and cargo test --workspace all pass — the same three gates CI runs.

Tests pin: level 1 against its original constants (regression lock for existing configs), strict monotonicity of all five ladders measured on glassify's output rather than on the table it reads, the Display ⇄ parser round trip for every level, the panel's full forward and reverse cycle, and restore-point integrity across a complete lap.

Not verified: the appearance of levels 4 and 5 on a real display. Levels 13 are unchanged from the merged implementation.

simota added 2 commits July 28, 2026 00:39
`glassmorphism` was a switch with one fixed look: you got the frosted
chrome the key shipped with, or you got nothing. The look itself was the
only thing anyone asked to change about it. Widen the key into a four-step
level — `off`, `1`, `2`, `3`, higher meaning more transparent — so the
glass can be dialed rather than only switched.

Level `1` is what `true` has always resolved to, exactly: `0.50` window
opacity, blur pinned to `64`, and the same backdrop / surface / pill / rim
values the chrome has carried since the key existed. Nobody's appearance
moves. Levels `2` and `3` step the window to `0.35` / `0.20` with every
chrome alpha following it down, and the rim mix climbing the other way —
a thinner face draws less of its own edge, so the stroke has to inherit
more of that job at each step. Blur stays at the key's maximum throughout;
there is no higher value left to give the more transparent levels, and
they need at least as much as level `1` does.

The takeover contract is unchanged in shape and only becomes level-keyed:
any on-level still owns `background-opacity` and `background-blur-radius`
outright, and the diagnostic that names a silenced key now quotes the pair
that level actually installs rather than a fixed one.

- `glassmorphism = true` / `false` still parse, to `1` / `off`, so no
  existing config file has to be touched
- Move the overlay alpha onto `ChromePalette` rather than deriving it from
  `is_glass()` at install time: with four variants the palette has to be
  the single source for every alpha, and a boolean can no longer say which
- Cycle the Settings row through all four steps, re-snapping the two rows
  the level manages on each one — each level owns a different pair, so the
  panel would otherwise display an opacity the next reload discards. The
  restore point stays the user's own values across those re-snaps

Claude-Session: https://claude.ai/code/session_0145t6595e9h4jGcNnxtp1gv
Three steps stopped short of what the key was widened for. `4` and `5`
carry it to the end: `0.12` and `0.06` window opacity, with the chrome
faces thinning to `0.040`/`0.020` backdrop and `0.034`/`0.014` surface.
These are panes you read *through* rather than *on* — at `5` the window
contributes six percent of its own pixels.

What keeps that reading as glass instead of as absence is the rim. A face
at `0.014` draws almost none of its own edge, so the stroke inherits the
rest: the rim mix climbs to `0.94` and then `1.00`, where the border *is*
the foreground color, the same value the pane's text is drawn in. That is
also the ceiling in the literal sense — there is nothing past "the edge is
as present as the content" — which is why the ladder stops at five.

The three lower levels are untouched, `1` still matches `glassmorphism =
true` exactly, and blur stays pinned at the key's maximum throughout.

- Give the pill alpha the shallowest ladder of the three: by `5` the
  backdrop has surrendered nine tenths of its opacity and the pill only
  three quarters, because a search pill that faded in step would go
  unreadable well before the panel behind it did
- Keep the top faces deliberately above zero; at `0.0` the sidebar stops
  being a plane and reads as text floating on the desktop, which is a
  different thing from more transparency
- Add `GlassLevel::ALL` / `ON_LEVELS` and route every enumeration through
  them. The panel's cycle and four tests spelled their levels out as
  slices, which — unlike a `match` — keep compiling when a variant is
  added and silently drop it off the end; level `4` was unreachable from
  the panel for exactly one build because of this
- Measure the rim ladder on `glassify`'s output rather than on the table
  `glassify` itself reads, so the assertion can actually fail; and pin
  the `Display`/parser round trip, which had no test tying the only
  writer of this key to its only reader

Claude-Session: https://claude.ai/code/session_0145t6595e9h4jGcNnxtp1gv

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e514191394

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
Self::Glassmorphism => {
"Frosted translucent sidebar and tab-overview chrome. Takes over window opacity and blur with its own recommended pair. Applies on save."
"Frosted translucent sidebar and tab-overview chrome, 4 steps (Off/1/2/3, higher = more transparent). Takes over window opacity and blur with the level's own recommended pair. Applies on save."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge List levels 4 and 5 in glassmorphism help

When a user focuses this Settings row, the displayed help says the cycle has only four states ending at level 3, but GlassLevel::ALL actually cycles through Off and levels 1–5. This makes the newly added levels 4 and 5 appear undocumented or accidental in the panel; describe the full Off/1/2/3/4/5 cycle and its five on-levels.

Useful? React with 👍 / 👎.

@simota
simota merged commit 21d4d62 into main Jul 27, 2026
1 check passed
@simota
simota deleted the feat/glassmorphism-levels branch July 27, 2026 16:02
@simota simota mentioned this pull request Jul 30, 2026
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