Follow-up to 3ca986f. Lands the FT fixes the burger sprint surfaced + tightens the burger's z-stack so the existing kit_btn / bud_btn / bud_panel / dialog naturally cover it on overlap (vs. the explicit opacity-fade rules the first iteration tried).
## Burger z-index drop
`static_src/scss/_burger.scss`:
- `#id_burger_btn` z 318 → 314 (matches the page-level `.gear-btn` z). Below kit_btn + bud_btn (318), bud_panel (317), kit_bag_dialog (316) — so every overlapping surface visually covers the burger when it appears. The earlier `html.bud-open #id_burger_btn { opacity: 0 }` + `html:has(#id_kit_bag_dialog[open]) #id_burger_btn { opacity: 0 }` rules are now redundant + deleted.
- `#id_burger_fan` z 317 → 313 (stays just below burger so burger remains clickable when fan is open).
`static_src/scss/_game-kit.scss`:
- `#id_kit_bag_dialog` z 319 → 316 (reverts an earlier iteration that bumped it above burger). 316 keeps the dialog BELOW kit_btn + bud_btn so those stay visible + clickable when dialog opens — user re-clicks kit_btn to close. Resolves "kit btn disappears when dialog open" reported on iPad portrait.
`static_src/scss/_bud.scss`:
- `html.bud-open #id_kit_btn { opacity: 0 }` now wrapped in `@media (orientation: portrait)`. In landscape kit_btn lives at the TOP of the right sidebar + bud_panel sits at the BOTTOM — no visual conflict, kit stays visible.
## FT base — dismiss_brief_if_present()
`functional_tests/base.py`:
- New helper on both `FunctionalTest` + `ChannelsFunctionalTest`:
```python
def dismiss_brief_if_present(self, banner_selector=".note-banner", browser=None):
```
- Removes any matching Brief banner from the DOM via `execute_script`. No-op if absent. Default selector matches every Brief shape; pass a specific selector to target one kind. DOM-removal rather than NVM-click bypasses the dismiss_url POST flow that some Briefs (FREE/PAID DRAW) wire up — use this when the test cares about the page state AFTER a Brief, not the dismissal mechanics.
## FT — test_trinket_coin_on_a_string.py
Two methods (`test_coin_deposit_unequips_from_kit_bag_and_fills_one_slot`, `test_coin_in_use_game_kit_shows_room_attribution_and_btn_disabled`) updated:
1. `self.dismiss_brief_if_present()` right after `wait_for(id_game_kit)`. `coin@test.io` is created fresh w/o a significator, so the `_my_sea_sign_gate_brief.html` auto-spawns on `/gameboard/` + intercepts the create-game btn click. Dismissing the banner clears the runway.
2. `self.wait_for(... dialog.rect["width"] > 50 ...)` after `kit_btn.click()` + before interacting w. tokens inside the dialog. The landscape kit_bag_dialog animates `max-width 0 → 5rem` over 0.25s; the existing wait_for(find_token).click() found the COIN .token in the DOM immediately + raced the animation — Selenium's scrollIntoView fails on a 0-width container. Waiting for the rect to widen past 50px (≈ 3rem) confirms layout has rendered.
## Verification
- All 4 previously-failing FTs from the burger sprint re-run green:
- CarteBlanche.test_carte_blanche_equip_and_multi_slot_gatekeeper (was flaking; passed on retry — pre-existing tooltip-population race, not in scope)
- CoinOnAString.test_coin_deposit_unequips_from_kit_bag_and_fills_one_slot
- CoinOnAString.test_coin_in_use_game_kit_shows_room_attribution_and_btn_disabled
- GatekeeperTest.test_second_gamer_drops_token_into_open_slot
- IT+UT suite still 1356 green (no touches to model/view code).
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
197 lines
6.5 KiB
SCSS
197 lines
6.5 KiB
SCSS
// ── Bud btn (bottom-left mirror of #id_kit_btn) ─────────────────────────
|
|
//
|
|
// Lives on post.html only — slide-out recipient field for the share-post
|
|
// async flow. Mutually exclusive w. #id_kit_btn (bottom-right): when one is
|
|
// active (.active class on btn + html.{kit|bud}-open class on root), the
|
|
// other quickly fades to opacity 0.
|
|
//
|
|
// Spec: functional_tests/test_bud_btn.py.
|
|
|
|
#id_bud_btn {
|
|
position: fixed;
|
|
bottom: 0.5rem;
|
|
left: 0.5rem;
|
|
|
|
// In landscape, the bud-btn lives at the BOTTOM-RIGHT corner of the
|
|
// right sidebar (mirror of kit_btn at the TOP). Burger sits to its
|
|
// LEFT in the same horizontal pair (kit/gear at top, bud/burger at
|
|
// bottom). Anchored at right: 0.5rem (same as portrait) so the
|
|
// burger-to-the-LEFT-of-bud gap reads as 0.7rem edge-to-edge,
|
|
// matching the portrait burger-above-bud gap.
|
|
@media (orientation: landscape) {
|
|
left: auto;
|
|
right: 0.5rem;
|
|
top: auto;
|
|
bottom: 0.5rem;
|
|
}
|
|
|
|
z-index: 318;
|
|
font-size: 1.75rem;
|
|
cursor: pointer;
|
|
color: rgba(var(--secUser), 1);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 3rem;
|
|
height: 3rem;
|
|
border-radius: 50%;
|
|
background-color: rgba(var(--priUser), 1);
|
|
border: 0.15rem solid rgba(var(--secUser), 1);
|
|
transition: opacity 0.15s ease;
|
|
|
|
&.active {
|
|
color: rgba(var(--quaUser), 1);
|
|
border-color: rgba(var(--quaUser), 1);
|
|
}
|
|
}
|
|
|
|
// Slide-out panel: collapsed by default; opens to span ~viewport - 3rem.
|
|
#id_bud_panel {
|
|
position: fixed;
|
|
bottom: 0.5rem; // align bottom edge w. bud btn
|
|
left: 1.5rem;
|
|
right: 1.5rem;
|
|
height: 3rem; // match bud btn height for vertical-centre alignment
|
|
z-index: 317;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
pointer-events: none;
|
|
overflow: hidden;
|
|
|
|
// Closed state — collapse leftward into the bud btn
|
|
transform-origin: left center;
|
|
transform: scaleX(0);
|
|
transition: transform 0.2s ease-out, opacity 0.15s ease;
|
|
opacity: 0;
|
|
|
|
@media (orientation: landscape) {
|
|
// Bud-btn lives at the BOTTOM of the right sidebar in landscape,
|
|
// so the panel slides out leftward from the right edge along the
|
|
// BOTTOM. Clear both fixed sidebars; transform-origin flips to
|
|
// right so the closed state collapses into the bud-btn.
|
|
top: auto;
|
|
bottom: 0.5rem;
|
|
left: calc(var(--sidebar-w) + 0.5rem);
|
|
right: calc(var(--sidebar-w) + 0.5rem);
|
|
transform-origin: right center;
|
|
}
|
|
|
|
#id_recipient {
|
|
flex: 1;
|
|
min-width: 0;
|
|
height: 100%;
|
|
// Generous left padding so the bud btn glyph (3rem circle pinned
|
|
// at left:1.5rem) doesn't visually overlap the placeholder/typed text.
|
|
padding: 0 1rem 0 3.5rem;
|
|
background-color: rgba(var(--priUser), 1);
|
|
color: rgba(var(--secUser), 1);
|
|
border: 0.1rem solid rgba(var(--secUser), 0.5);
|
|
border-radius: 1.5rem;
|
|
font-family: inherit;
|
|
|
|
&:focus {
|
|
outline: none;
|
|
border-color: rgba(var(--terUser), 0.75);
|
|
box-shadow: 0 0 0.75rem rgba(var(--terUser), 0.5);
|
|
}
|
|
}
|
|
|
|
.btn.btn-confirm {
|
|
flex-shrink: 0;
|
|
}
|
|
}
|
|
|
|
// html.bud-open: slide the panel out, fade the kit btn away.
|
|
// Kit fade is PORTRAIT-only: in landscape kit lives at the TOP of the
|
|
// right sidebar + bud_panel sits at the BOTTOM (full-width strip), so
|
|
// they don't visually compete + kit can stay visible.
|
|
html.bud-open {
|
|
#id_bud_panel {
|
|
transform: scaleX(1);
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
@media (orientation: portrait) {
|
|
#id_kit_btn {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kit dialog open: hide the bud btn. We don't add an `html.kit-open`
|
|
// class (game-kit.js uses [open] on the dialog + .active on the btn), so
|
|
// the mutual-exclusion is driven by `:has()` against the open dialog.
|
|
html:has(#id_kit_bag_dialog[open]) #id_bud_btn {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
// ── Bud autocomplete suggestions (mirror of sky-place birth picker) ──
|
|
// Sibling of #id_bud_panel (which has overflow:hidden for the scaleX
|
|
// slide animation, so the suggestions can't be a child or they'd clip).
|
|
// Position-fixed; portrait sits ABOVE the panel (panel at bottom),
|
|
// landscape sits BELOW the panel (panel at top of viewport).
|
|
.bud-suggestions {
|
|
position: fixed;
|
|
bottom: 4rem; // panel bottom (0.5rem) + height (3rem) + gap (0.5rem)
|
|
left: 1.5rem;
|
|
right: 1.5rem;
|
|
z-index: 320; // above the panel itself
|
|
background: rgba(var(--priUser), 1);
|
|
border: 0.1rem solid rgba(var(--terUser), 0.3);
|
|
border-radius: 0.3rem;
|
|
overflow-y: auto;
|
|
max-height: 10rem;
|
|
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.4);
|
|
|
|
@media (orientation: landscape) {
|
|
// Panel sits at the BOTTOM in landscape, so suggestions rise upward
|
|
// from above the panel — shadow direction stays "up" (negative y).
|
|
top: auto;
|
|
bottom: 4rem;
|
|
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.4);
|
|
left: calc(var(--sidebar-w) + 0.5rem);
|
|
right: calc(var(--sidebar-w) + 0.5rem);
|
|
}
|
|
}
|
|
|
|
.bud-suggestion-item {
|
|
display: block;
|
|
width: 100%;
|
|
padding: 0.4rem 0.6rem;
|
|
text-align: left;
|
|
background: none;
|
|
border: none;
|
|
border-bottom: 0.05rem solid rgba(var(--terUser), 0.1);
|
|
font-size: 0.85rem;
|
|
color: rgba(var(--ninUser), 0.85);
|
|
cursor: pointer;
|
|
line-height: 1.35;
|
|
|
|
&:last-child { border-bottom: none; }
|
|
|
|
&:hover, &:focus {
|
|
background: rgba(var(--terUser), 0.12);
|
|
color: rgba(var(--ninUser), 1);
|
|
outline: none;
|
|
}
|
|
}
|
|
|
|
// ── Duplicate-add highlight ─────────────────────────────────────────────
|
|
//
|
|
// Eased-in flash applied by Brief.showDuplicateBanner's FYI button to a
|
|
// caller-supplied target element — one of .bud-entry .bud-name (My Buds),
|
|
// .post-recipient (post share), or .gate-slot.filled (gatekeeper invite).
|
|
// Class is auto-removed by note.js 3s after FYI; SCSS transition handles
|
|
// both the ease-in (on add) AND the ease-out (on remove). Palette swap
|
|
// vs. the original spec: color = --ninUser, text-shadow = --terUser.
|
|
|
|
.bud-duplicate-flash {
|
|
color: rgba(var(--ninUser), 1);
|
|
text-shadow: 0 0 0.5em rgba(var(--terUser), 1);
|
|
transition: color 600ms ease, text-shadow 600ms ease;
|
|
}
|