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
33 changes: 20 additions & 13 deletions apps/web/src/phone-skeleton.css
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,14 @@
var(--phone-tab-row-max)
);
margin-inline: auto;
gap: var(--phone-tab-bar-gap);
align-items: center;
gap: var(--phone-tab-action-gap);
}

.phone-skeleton-bar,
.phone-skeleton-action {
height: var(--phone-tab-bar-height);
border-radius: var(--radius-full);
border-radius: var(--radius-phone-tab-bar);
background: var(--color-glass);
-webkit-backdrop-filter: var(--glass-filter);
backdrop-filter: var(--glass-filter);
Expand All @@ -140,15 +141,15 @@
display: grid;
flex: 1;
grid-template-columns: repeat(4, minmax(0, 1fr));
padding: var(--spacing);
padding: var(--phone-tab-bar-inset);

/* Each tab: an icon-sized mark over a short label. */
& > span {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: calc(var(--spacing) * 1.5);
gap: calc(var(--spacing) * 0.75);
}

& > span::before,
Expand All @@ -167,18 +168,24 @@

& > span::after {
width: calc(var(--spacing) * 9);
height: calc(var(--spacing) * 2);
height: var(--phone-tab-label-line);
}
}

.phone-skeleton-action {
flex: none;
width: var(--phone-tab-bar-height);
width: var(--phone-tab-action-size);
height: var(--phone-tab-action-size);
border-radius: var(--radius-full);
}

@media (max-width: 359px) {
.phone-skeleton {
--phone-tab-action-gap: calc(var(--spacing) * 1.5);
--phone-tab-action-size: calc(var(--spacing) * 12);
}

.phone-skeleton-tabs {
gap: calc(var(--spacing) * 1.5);
width: calc(100% - var(--spacing) * 4);
}
}
Expand All @@ -188,15 +195,15 @@
.phone-skeleton {
--phone-bar-height: calc(var(--spacing) * 9);
--phone-tab-bar-height: calc(var(--control-target-coarse) + var(--spacing));
--phone-tab-bar-inset: var(--phone-tab-bar-inset-compact);
--phone-tab-bar-gap: calc(var(--spacing) * 1.5);
--phone-tab-action-gap: calc(var(--spacing) * 1.5);
--phone-tab-action-size: var(--phone-tab-bar-height);
}

.phone-skeleton-bar {
padding: calc(var(--spacing) / 2);

& > span {
flex-direction: row;
}
.phone-skeleton-bar > span {
flex-direction: row;
gap: calc(var(--spacing) * 1.5);
}
}

Expand Down
146 changes: 118 additions & 28 deletions apps/web/src/phone-workspace.css
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,10 @@
);
margin-inline: auto;
display: flex;
/* The action sits centred beside the bar; both shrink to the same circle size when minimised. */
align-items: center;
gap: var(--phone-tab-bar-gap);
justify-content: space-between;
gap: var(--phone-tab-action-gap);
transition: transform var(--tab-slide-dur) var(--tab-slide-ease), visibility 0s;
}

Expand All @@ -277,11 +279,73 @@
.phone-tab-bar {
position: relative;
display: grid;
flex: 1;
flex: none;
grid-template-columns: repeat(var(--phone-tab-count, 3), minmax(0, 1fr));
width: 100%;
height: var(--phone-tab-bar-height);
padding: var(--spacing);
border-radius: var(--radius-full);
padding: var(--phone-tab-bar-inset);
border-radius: var(--radius-phone-tab-bar);
transition:
width var(--tab-slide-dur) var(--tab-slide-ease),
height var(--tab-slide-dur) var(--tab-slide-ease),
border-radius var(--tab-slide-dur) var(--tab-slide-ease);
}

/* A width rather than flex growth, so minimising can animate it. */
.phone-tab-row:has(> .phone-tab-action) > .phone-tab-bar {
width: calc(100% - var(--phone-tab-action-size) - var(--phone-tab-action-gap));
}

/*
* Reading down a pane minimises the bar to one circle that shows the current tab, as the top
* bar tucks away on the same scroll. Scrolling up, focus, or a tap on the circle restores it.
*/
.phone-tab-row[data-phone-tab-minimised] > .phone-tab-bar {
width: var(--phone-tab-bar-minimised);
height: var(--phone-tab-bar-minimised);
border-radius: var(--radius-phone-tab-minimised);
}

.phone-tab-row[data-phone-tab-minimised] > .phone-tab-action {
width: var(--phone-tab-bar-minimised);
height: var(--phone-tab-bar-minimised);
}

.phone-tab-row[data-phone-tab-minimised] :is(.phone-tab, .phone-tab-indicator) {
opacity: 0;
pointer-events: none;
}

.phone-tab-mini {
position: absolute;
z-index: 2;
inset: 0;
display: grid;
place-items: center;
color: var(--color-brand-ink);
opacity: 0;
pointer-events: none;
touch-action: manipulation;
transition: opacity var(--duration-fast) var(--ease-out);
}

.phone-tab-mini > .phone-tab-badge {
top: calc(var(--spacing) * -1);
right: calc(var(--spacing) * -1);
left: auto;
}

/* Inset far enough that the dot sits inside the circle's curve, not on its edge. */
.phone-tab-mini > .phone-tab-dot {
top: calc(var(--spacing) * 2);
right: calc(var(--spacing) * 2);
}

.phone-tab-row[data-phone-tab-minimised] .phone-tab-mini {
opacity: 1;
pointer-events: auto;
/* The circle's icon arrives once the tabs have faded, not over them. */
transition-delay: var(--duration-fast);
}

.phone-tab-bar[hidden] {
Expand Down Expand Up @@ -352,13 +416,15 @@

.phone-tab-indicator {
position: absolute;
inset-block: var(--spacing);
left: var(--spacing);
width: calc((100% - var(--spacing) * 2) / var(--phone-tab-count, 3));
border-radius: var(--radius-full);
inset-block: var(--phone-tab-bar-inset);
left: var(--phone-tab-bar-inset);
width: calc((100% - var(--phone-tab-bar-inset) * 2) / var(--phone-tab-count, 3));
border-radius: var(--radius-phone-tab-pill);
background: var(--color-selected);
transform: translateX(calc(var(--phone-tab-index, 0) * 100%));
transition: transform var(--tab-slide-dur) var(--tab-slide-ease);
transition:
transform var(--tab-slide-dur) var(--tab-slide-ease),
opacity var(--duration-fast) var(--ease-out);
}

.phone-tab {
Expand All @@ -369,16 +435,20 @@
flex-direction: column;
align-items: center;
justify-content: center;
gap: calc(var(--spacing) / 2);
border-radius: var(--radius-full);
color: var(--color-text-tertiary);
font-size: var(--text-2xs);
font-weight: var(--font-weight-medium);
gap: calc(var(--spacing) * 0.75);
border-radius: var(--radius-phone-tab-pill);
color: var(--color-text-secondary);
font-size: var(--phone-tab-label-size);
font-weight: var(--font-weight-semibold);
line-height: var(--phone-tab-label-line);
letter-spacing: 0.01em;
touch-action: manipulation;
user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
transition: color var(--duration-base) var(--ease-out);
transition:
color var(--duration-base) var(--ease-out),
opacity var(--duration-fast) var(--ease-out);
}

.phone-tab[aria-current="page"] {
Expand All @@ -389,19 +459,24 @@
opacity: var(--opacity-quiet);
}

/* The narrowest phones give up gaps and label tracking, never target size. */
/* The narrowest phones give up gaps, label tracking and some of the action, never target size. */
@media (max-width: 359px) {
.workspace-root[data-workspace-phone] {
--phone-tab-action-gap: calc(var(--spacing) * 1.5);
--phone-tab-action-size: calc(var(--spacing) * 12);
}

.phone-tab-row {
gap: calc(var(--spacing) * 1.5);
width: calc(100% - var(--spacing) * 4);
}

.phone-tab-label {
letter-spacing: -0.02em;
letter-spacing: -0.01em;
}
}

.phone-tab [data-nucleo-icon],
.phone-tab-mini [data-nucleo-icon],
.phone-tab-action [data-nucleo-icon] {
color: inherit;
}
Expand Down Expand Up @@ -445,11 +520,14 @@
display: grid;
flex: none;
place-items: center;
width: var(--phone-tab-bar-height);
height: var(--phone-tab-bar-height);
width: var(--phone-tab-action-size);
height: var(--phone-tab-action-size);
border-radius: var(--radius-full);
color: var(--color-text-primary);
touch-action: manipulation;
transition:
width var(--tab-slide-dur) var(--tab-slide-ease),
height var(--tab-slide-dur) var(--tab-slide-ease);
}

/* Landscape: a short window keeps chrome low, with labels beside their icons. */
Expand All @@ -458,22 +536,23 @@
--phone-bar-height: calc(var(--spacing) * 9);
/* Tabs keep the full 44px target inside the bar's 2px inset. */
--phone-tab-bar-height: calc(var(--control-target-coarse) + var(--spacing));
--phone-tab-bar-inset: var(--phone-tab-bar-inset-compact);
--phone-tab-bar-gap: calc(var(--spacing) * 1.5);
--phone-tab-action-gap: calc(var(--spacing) * 1.5);
--phone-tab-action-size: var(--phone-tab-bar-height);
--phone-tab-bar-minimised: var(--phone-tab-bar-height);
}

.phone-tab-bar {
padding: calc(var(--spacing) / 2);
}

.phone-tab-indicator {
inset-block: calc(var(--spacing) / 2);
left: calc(var(--spacing) / 2);
width: calc((100% - var(--spacing)) / var(--phone-tab-count, 3));
/* The bar is near a capsule at this height, and the pill stays concentric inside the narrower inset. */
:is(.phone-tab-indicator, .phone-tab) {
border-radius: var(--radius-phone-tab-pill-compact);
}

.phone-tab {
flex-direction: row;
gap: calc(var(--spacing) * 1.5);
font-size: var(--text-2xs);
letter-spacing: normal;
}

.phone-tab-icon [data-nucleo-icon] {
Expand All @@ -486,6 +565,15 @@
display: none;
}

.phone-tab-mini > .phone-tab-badge {
display: block;
}

/* The wide bar shrinks toward its action, so the circle and the action stay together. */
.phone-tab-row:has(> .phone-tab-action) > .phone-tab-bar {
margin-inline-start: auto;
}

.phone-tab-badge-inline {
display: inline-flex;
}
Expand Down Expand Up @@ -562,6 +650,8 @@
.workspace-root[data-workspace-phone] .workspace-stack-bar,
.phone-tab-indicator,
.phone-tab-row,
.phone-tab-bar,
.phone-tab-action,
.phone-tab-action > svg {
transition: none;
}
Expand Down
Loading
Loading