fix(omnibox): strengthen scalable outer borders (#439)
* fix(omnibox): strengthen scalable outer borders * test(omnibox): verify scalable border hierarchy
- commit
- fc90745d39c798510bf444be79963be8f53892bf
- author
- bnema · · 2026-09-15 04:44 UTC
- committer
- GitHub · · 2026-09-15 04:44 UTC
- parent
- 6f391e8
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown
3 files · +34 -1
M CHANGELOG.md +1 -0
| @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. | |||
| 13 | 13 | ||
| 14 | 14 | ### Changed | |
| 15 | 15 | ||
| 16 | + | - **Omnibox borders**: Outer borders scale with the configured UI size and remain more pronounced for the default `command` style. | |
| 16 | 17 | - **Omnibox status indicators**: The history-order control is now a passive status badge switched with `Ctrl+R`, and favorites use a terminal-style `*` marker. | |
| 17 | 18 | - **Favorites organization**: Favorites now use a tags-first model. Existing folder assignments are migrated into deterministic tags, with collision handling and folded-name uniqueness preserved during migration. | |
| 18 | 19 | - **Page mode configuration**: Page Mode is now called Vim Mode and uses the `workspace.vim_mode` configuration namespace for activation, timeouts, and action sequences. Existing page-mode bindings are migrated by the configuration loader. | |
| @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. | |||||
| 13 | 13 | ||||
| 14 | ### Changed | 14 | ### Changed | ||
| 15 | 15 | ||||
| 16 | + | - **Omnibox borders**: Outer borders scale with the configured UI size and remain more pronounced for the default `command` style. | |||
| 16 | - **Omnibox status indicators**: The history-order control is now a passive status badge switched with `Ctrl+R`, and favorites use a terminal-style `*` marker. | 17 | - **Omnibox status indicators**: The history-order control is now a passive status badge switched with `Ctrl+R`, and favorites use a terminal-style `*` marker. | ||
| 17 | - **Favorites organization**: Favorites now use a tags-first model. Existing folder assignments are migrated into deterministic tags, with collision handling and folded-name uniqueness preserved during migration. | 18 | - **Favorites organization**: Favorites now use a tags-first model. Existing folder assignments are migrated into deterministic tags, with collision handling and folded-name uniqueness preserved during migration. | ||
| 18 | - **Page mode configuration**: Page Mode is now called Vim Mode and uses the `workspace.vim_mode` configuration namespace for activation, timeouts, and action sequences. Existing page-mode bindings are migrated by the configuration loader. | 19 | - **Page mode configuration**: Page Mode is now called Vim Mode and uses the `workspace.vim_mode` configuration namespace for activation, timeouts, and action sequences. Existing page-mode bindings are migrated by the configuration loader. | ||
M internal/ui/theme/css.go +2 -1
| @@ -242,7 +242,7 @@ window.standalone-omnibox-window { | |||
| 242 | 242 | .omnibox-container { | |
| 243 | 243 | background-color: var(--surface); | |
| 244 | 244 | background-image: none; | |
| 245 | - | border: 0.0625em solid alpha(var(--border), 0.85); | |
| 245 | + | border: 0.09375em solid alpha(var(--border), 0.9); | |
| 246 | 246 | border-radius: 0.1875em; | |
| 247 | 247 | box-shadow: 0 0.875em 2.5em alpha(black, 0.24), 0 0.0625em 0 alpha(white, 0.05) inset; | |
| 248 | 248 | padding: 0; | |
| @@ -577,6 +577,7 @@ entry.omnibox-entry.omnibox-entry-bang-active:focus-visible > text { | |||
| 577 | 577 | ||
| 578 | 578 | /* Command: a calmer command palette with a floating input and compact tabs. */ | |
| 579 | 579 | .omnibox-container.omnibox-style-command { | |
| 580 | + | border: 0.125em solid alpha(var(--border), 0.95); | |
| 580 | 581 | border-radius: 0.625em; | |
| 581 | 582 | box-shadow: 0 1.25em 3.5em alpha(black, 0.38), 0 0 0 0.0625em alpha(white, 0.04) inset; | |
| 582 | 583 | padding: 0.375em; | |
| @@ -242,7 +242,7 @@ window.standalone-omnibox-window { | |||||
| 242 | .omnibox-container { | 242 | .omnibox-container { | ||
| 243 | background-color: var(--surface); | 243 | background-color: var(--surface); | ||
| 244 | background-image: none; | 244 | background-image: none; | ||
| 245 | - | border: 0.0625em solid alpha(var(--border), 0.85); | 245 | + | border: 0.09375em solid alpha(var(--border), 0.9); |
| 246 | border-radius: 0.1875em; | 246 | border-radius: 0.1875em; | ||
| 247 | box-shadow: 0 0.875em 2.5em alpha(black, 0.24), 0 0.0625em 0 alpha(white, 0.05) inset; | 247 | box-shadow: 0 0.875em 2.5em alpha(black, 0.24), 0 0.0625em 0 alpha(white, 0.05) inset; | ||
| 248 | padding: 0; | 248 | padding: 0; | ||
| @@ -577,6 +577,7 @@ entry.omnibox-entry.omnibox-entry-bang-active:focus-visible > text { | |||||
| 577 | 577 | ||||
| 578 | /* Command: a calmer command palette with a floating input and compact tabs. */ | 578 | /* Command: a calmer command palette with a floating input and compact tabs. */ | ||
| 579 | .omnibox-container.omnibox-style-command { | 579 | .omnibox-container.omnibox-style-command { | ||
| 580 | + | border: 0.125em solid alpha(var(--border), 0.95); | |||
| 580 | border-radius: 0.625em; | 581 | border-radius: 0.625em; | ||
| 581 | box-shadow: 0 1.25em 3.5em alpha(black, 0.38), 0 0 0 0.0625em alpha(white, 0.04) inset; | 582 | box-shadow: 0 1.25em 3.5em alpha(black, 0.38), 0 0 0 0.0625em alpha(white, 0.04) inset; | ||
| 582 | padding: 0.375em; | 583 | padding: 0.375em; | ||
M internal/ui/theme/css_test.go +31 -0
| @@ -2,6 +2,7 @@ package theme | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "regexp" | |
| 5 | + | "strconv" | |
| 5 | 6 | "strings" | |
| 6 | 7 | "testing" | |
| 7 | 8 | ||
| @@ -251,6 +252,36 @@ func TestGenerateCSSWithScale_UsesMediumWeightForOmniboxSuggestionTitle(t *testi | |||
| 251 | 252 | assert.NotContains(t, css, ".omnibox-suggestion-title {\n\tfont-size: 0.875em;\n\tcolor: var(--text);\n\tfont-weight: 400;") | |
| 252 | 253 | } | |
| 253 | 254 | ||
| 255 | + | func TestGenerateCSS_OmniboxOuterBordersUseScalableEmUnits(t *testing.T) { | |
| 256 | + | css := GenerateCSS(DefaultDarkPalette()) | |
| 257 | + | ||
| 258 | + | base := cssRuleBlock(t, css, ".omnibox-container") | |
| 259 | + | baseWidth := cssBorderWidthEm(t, base) | |
| 260 | + | assert.InDelta(t, 0.09375, baseWidth, 0.000001) | |
| 261 | + | ||
| 262 | + | command := cssRuleBlock(t, css, ".omnibox-container.omnibox-style-command") | |
| 263 | + | commandWidth := cssBorderWidthEm(t, command) | |
| 264 | + | assert.Greater(t, commandWidth, baseWidth) | |
| 265 | + | ||
| 266 | + | for _, style := range []string{"multiplexer", "minimal"} { | |
| 267 | + | block := cssRuleBlock(t, css, ".omnibox-container.omnibox-style-"+style) | |
| 268 | + | assert.NotRegexp(t, `(?m)^\s*border(?:-width)?:`, block) | |
| 269 | + | } | |
| 270 | + | } | |
| 271 | + | ||
| 272 | + | func cssBorderWidthEm(t *testing.T, block string) float64 { | |
| 273 | + | t.Helper() | |
| 274 | + | match := regexp.MustCompile(`(?m)^\s*border:\s*([0-9.]+)em\s`).FindStringSubmatch(block) | |
| 275 | + | if len(match) != 2 { | |
| 276 | + | t.Fatalf("CSS rule does not define an em border: %s", block) | |
| 277 | + | } | |
| 278 | + | width, err := strconv.ParseFloat(match[1], 64) | |
| 279 | + | if err != nil { | |
| 280 | + | t.Fatalf("parse border width %q: %v", match[1], err) | |
| 281 | + | } | |
| 282 | + | return width | |
| 283 | + | } | |
| 284 | + | ||
| 254 | 285 | func TestGenerateCSS_OmniboxIncludesVisualConcepts(t *testing.T) { | |
| 255 | 286 | css := GenerateCSS(DefaultDarkPalette()) | |
| 256 | 287 | ||
| @@ -2,6 +2,7 @@ package theme | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "regexp" | 4 | "regexp" | ||
| 5 | + | "strconv" | |||
| 5 | "strings" | 6 | "strings" | ||
| 6 | "testing" | 7 | "testing" | ||
| 7 | 8 | ||||
| @@ -251,6 +252,36 @@ func TestGenerateCSSWithScale_UsesMediumWeightForOmniboxSuggestionTitle(t *testi | |||||
| 251 | assert.NotContains(t, css, ".omnibox-suggestion-title {\n\tfont-size: 0.875em;\n\tcolor: var(--text);\n\tfont-weight: 400;") | 252 | assert.NotContains(t, css, ".omnibox-suggestion-title {\n\tfont-size: 0.875em;\n\tcolor: var(--text);\n\tfont-weight: 400;") | ||
| 252 | } | 253 | } | ||
| 253 | 254 | ||||
| 255 | + | func TestGenerateCSS_OmniboxOuterBordersUseScalableEmUnits(t *testing.T) { | |||
| 256 | + | css := GenerateCSS(DefaultDarkPalette()) | |||
| 257 | + | ||||
| 258 | + | base := cssRuleBlock(t, css, ".omnibox-container") | |||
| 259 | + | baseWidth := cssBorderWidthEm(t, base) | |||
| 260 | + | assert.InDelta(t, 0.09375, baseWidth, 0.000001) | |||
| 261 | + | ||||
| 262 | + | command := cssRuleBlock(t, css, ".omnibox-container.omnibox-style-command") | |||
| 263 | + | commandWidth := cssBorderWidthEm(t, command) | |||
| 264 | + | assert.Greater(t, commandWidth, baseWidth) | |||
| 265 | + | ||||
| 266 | + | for _, style := range []string{"multiplexer", "minimal"} { | |||
| 267 | + | block := cssRuleBlock(t, css, ".omnibox-container.omnibox-style-"+style) | |||
| 268 | + | assert.NotRegexp(t, `(?m)^\s*border(?:-width)?:`, block) | |||
| 269 | + | } | |||
| 270 | + | } | |||
| 271 | + | ||||
| 272 | + | func cssBorderWidthEm(t *testing.T, block string) float64 { | |||
| 273 | + | t.Helper() | |||
| 274 | + | match := regexp.MustCompile(`(?m)^\s*border:\s*([0-9.]+)em\s`).FindStringSubmatch(block) | |||
| 275 | + | if len(match) != 2 { | |||
| 276 | + | t.Fatalf("CSS rule does not define an em border: %s", block) | |||
| 277 | + | } | |||
| 278 | + | width, err := strconv.ParseFloat(match[1], 64) | |||
| 279 | + | if err != nil { | |||
| 280 | + | t.Fatalf("parse border width %q: %v", match[1], err) | |||
| 281 | + | } | |||
| 282 | + | return width | |||
| 283 | + | } | |||
| 284 | + | ||||
| 254 | func TestGenerateCSS_OmniboxIncludesVisualConcepts(t *testing.T) { | 285 | func TestGenerateCSS_OmniboxIncludesVisualConcepts(t *testing.T) { | ||
| 255 | css := GenerateCSS(DefaultDarkPalette()) | 286 | css := GenerateCSS(DefaultDarkPalette()) | ||
| 256 | 287 | ||||