Merge branch 'worktree-agent-aed4618fbd0b6aeac'
This commit is contained in:
@@ -10,8 +10,9 @@
|
||||
* NavigationRailHorizontalItemTokens.kt, NavigationRailColorTokens.kt, and the layout in
|
||||
* ShortNavigationBar.kt, WideNavigationRail.kt and NavigationItem.kt.
|
||||
*
|
||||
* [data-navigation-bar] surface-container, 64px, the bottom safe area under it
|
||||
* [data-navigation-bar-items] equal widths; centred from a 600px-wide bar
|
||||
* [data-navigation-bar] surface-container, 64px (data-tall: 80px), the bottom
|
||||
* safe area under it
|
||||
* [data-navigation-bar-items] equal widths; centred from a 600px-wide short bar
|
||||
* [data-navigation-bar-item] data-active
|
||||
* [data-navigation-pill] icon and label; the indicator itself from 600px
|
||||
* [data-navigation-indicator] the 56×32 indicator around the icon below 600px
|
||||
@@ -42,16 +43,19 @@
|
||||
* rail starts expanded instead of collapsed. The bar's own item layout is a *container* query at
|
||||
* the same 37.5rem, so a bar in a narrow column lays out by its own width.
|
||||
*
|
||||
* Everything here is in `@layer components` except the last block, which restyles a FAB the
|
||||
* application put in the rail: that one has to beat a utility, and a layer never does.
|
||||
* Everything here is in `@layer components` except the blocks after it, which reach past the
|
||||
* component into what an application wrote — the bottom offset <x-app-shell> publishes, a FAB put
|
||||
* in the rail: those have to beat a utility, and a layer never does.
|
||||
*/
|
||||
|
||||
/* Every branch below reads `:not([data-open])`: a rail open over a scrim is drawn expanded whatever
|
||||
made it collapsed, which only `hide-when-collapsed` can bring about outside the modal modes. */
|
||||
@custom-variant rail-collapsed {
|
||||
&:where([data-navigation-rail='collapsed'], [data-navigation-rail='collapsed'] *) {
|
||||
@slot;
|
||||
}
|
||||
|
||||
&:where([data-rail='collapsed'] [data-navigation-rail='collapsible'], [data-rail='collapsed'] [data-navigation-rail='collapsible'] *) {
|
||||
&:where([data-rail='collapsed'] [data-navigation-rail='collapsible']:not([data-open]), [data-rail='collapsed'] [data-navigation-rail='collapsible']:not([data-open]) *) {
|
||||
@slot;
|
||||
}
|
||||
|
||||
@@ -62,7 +66,7 @@
|
||||
/* Below `medium` a 220–360px rail would be most of the screen, so a rail whose width is the
|
||||
visitor's choice is held collapsed there whatever they chose. */
|
||||
@media (width < 37.5rem) {
|
||||
&:where([data-navigation-rail='collapsible'], [data-navigation-rail='collapsible'] *) {
|
||||
&:where([data-navigation-rail='collapsible']:not([data-open]), [data-navigation-rail='collapsible']:not([data-open]) *) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
@@ -78,14 +82,14 @@
|
||||
`data-rail-auto`, which <x-theme-script> sets while nothing is stored, stands for "no
|
||||
choice yet" and the class's own default applies. */
|
||||
@media (52.5rem <= width < 75rem) {
|
||||
&:where(:is([data-rail='collapsed'], [data-rail-auto]) [data-navigation-rail='adaptive'], :is([data-rail='collapsed'], [data-rail-auto]) [data-navigation-rail='adaptive'] *) {
|
||||
&:where(:is([data-rail='collapsed'], [data-rail-auto]) [data-navigation-rail='adaptive']:not([data-open]), :is([data-rail='collapsed'], [data-rail-auto]) [data-navigation-rail='adaptive']:not([data-open]) *) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
/* Large and extra-large (from 1200): expanded to begin with, which is what M3 prefers there. */
|
||||
@media (width >= 75rem) {
|
||||
&:where([data-rail='collapsed'] [data-navigation-rail='adaptive'], [data-rail='collapsed'] [data-navigation-rail='adaptive'] *) {
|
||||
&:where([data-rail='collapsed'] [data-navigation-rail='adaptive']:not([data-open]), [data-rail='collapsed'] [data-navigation-rail='adaptive']:not([data-open]) *) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
@@ -108,6 +112,26 @@
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
/* The tall configuration: NavigationBarTokens.TallContainerHeight, 80dp. It keeps the vertical
|
||||
item layout at every width — the horizontal one below is the short bar's alone — so the
|
||||
label always sits under its icon, which is what the extra 16px is for. */
|
||||
[data-navigation-bar][data-tall] [data-navigation-bar-items] {
|
||||
min-height: 5rem;
|
||||
}
|
||||
|
||||
/* "Scrolling: hides on scroll-down, reappears on scroll-up" (the reference's Navigation bar
|
||||
§ Behaviour). It slides out of the window on the default spatial spring, which reduced
|
||||
motion zeroes to an instant swap along with every other duration token. Whether it may hide
|
||||
at all is resources/js/navigation.js's call: never while a bottom sheet, a drawer or a
|
||||
snackbar is on screen, since those are anchored to the bar's edge. */
|
||||
[data-navigation-bar][data-hide-on-scroll] {
|
||||
transition: translate var(--md-sys-motion-spatial-default-duration) var(--md-sys-motion-spatial-default);
|
||||
}
|
||||
|
||||
[data-navigation-bar][data-hide-on-scroll][data-hidden] {
|
||||
translate: 0 100%;
|
||||
}
|
||||
|
||||
[data-navigation-bar-item] {
|
||||
--navigation-layer: 0;
|
||||
position: relative;
|
||||
@@ -149,22 +173,24 @@
|
||||
}
|
||||
|
||||
/* From 600dp (M3's medium window), icon and label side by side in a 40px indicator, and the
|
||||
items centred with the padding ShortNavigationBar's Centered arrangement computes. */
|
||||
items centred with the padding ShortNavigationBar's Centered arrangement computes. The
|
||||
short bar only: a tall bar is the vertical layout at every width, so every rule here that
|
||||
the vertical layout also has an opinion about names the short bar. */
|
||||
@container (width >= 37.5rem) {
|
||||
[data-navigation-bar-items] {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-items] {
|
||||
width: calc(10% * (var(--navigation-bar-count, 7) + 3));
|
||||
min-width: fit-content;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
[data-navigation-bar-items]:has(> :last-child:nth-child(1)) { --navigation-bar-count: 1; }
|
||||
[data-navigation-bar-items]:has(> :last-child:nth-child(2)) { --navigation-bar-count: 2; }
|
||||
[data-navigation-bar-items]:has(> :last-child:nth-child(3)) { --navigation-bar-count: 3; }
|
||||
[data-navigation-bar-items]:has(> :last-child:nth-child(4)) { --navigation-bar-count: 4; }
|
||||
[data-navigation-bar-items]:has(> :last-child:nth-child(5)) { --navigation-bar-count: 5; }
|
||||
[data-navigation-bar-items]:has(> :last-child:nth-child(6)) { --navigation-bar-count: 6; }
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-items]:has(> :last-child:nth-child(1)) { --navigation-bar-count: 1; }
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-items]:has(> :last-child:nth-child(2)) { --navigation-bar-count: 2; }
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-items]:has(> :last-child:nth-child(3)) { --navigation-bar-count: 3; }
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-items]:has(> :last-child:nth-child(4)) { --navigation-bar-count: 4; }
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-items]:has(> :last-child:nth-child(5)) { --navigation-bar-count: 5; }
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-items]:has(> :last-child:nth-child(6)) { --navigation-bar-count: 6; }
|
||||
|
||||
[data-navigation-bar-item] {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item] {
|
||||
min-width: max-content;
|
||||
}
|
||||
|
||||
@@ -172,7 +198,7 @@
|
||||
token, and Compose's ShortNavigationBarItem passes it for both icon positions. (The
|
||||
*rail's* horizontal item is label-large — NavigationRailHorizontalItemTokens — which is
|
||||
a different component.) */
|
||||
[data-navigation-bar-item] [data-navigation-pill] {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item] [data-navigation-pill] {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
flex-direction: row;
|
||||
@@ -180,15 +206,15 @@
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
|
||||
[data-navigation-bar-item] [data-navigation-label] {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item] [data-navigation-label] {
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
[data-navigation-bar-item][data-active] {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item][data-active] {
|
||||
color: var(--md-sys-color-on-secondary-container);
|
||||
}
|
||||
|
||||
[data-navigation-bar-item][data-active] [data-navigation-pill] {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item][data-active] [data-navigation-pill] {
|
||||
background-image: linear-gradient(var(--md-sys-color-secondary-container), var(--md-sys-color-secondary-container));
|
||||
}
|
||||
}
|
||||
@@ -197,25 +223,61 @@
|
||||
|
||||
[data-navigation-rail] {
|
||||
--navigation-rail-expanded-width: clamp(13.75rem, var(--navigation-rail-width, 16rem), 22.5rem);
|
||||
--navigation-rail-collapsed-width: 6rem;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: var(--navigation-rail-expanded-width);
|
||||
transition: width var(--md-sys-motion-spatial-default-duration) var(--md-sys-motion-spatial-default);
|
||||
|
||||
@variant rail-collapsed {
|
||||
width: 6rem;
|
||||
width: var(--navigation-rail-collapsed-width);
|
||||
}
|
||||
}
|
||||
|
||||
/* M3's other collapsed width: NavigationRailCollapsedTokens.NarrowContainerWidth, 80dp against
|
||||
the default 96. A variable rather than a width here, so the rules below — which each have
|
||||
their own reason to name a collapsed rail's width — do not have to know about it. */
|
||||
[data-navigation-rail][data-width='narrow'] {
|
||||
--navigation-rail-collapsed-width: 5rem;
|
||||
}
|
||||
|
||||
/* "Expanded behavior | Hide when collapsed" (the reference's Navigation rail § Variants and
|
||||
configurations): instead of narrowing, the rail leaves the layout entirely — for an
|
||||
immersive page — and comes back over it when a menu button somewhere else, the app bar's,
|
||||
calls `$store.rail.show()`. It is the *expanded* rail that hides, which is why what comes
|
||||
back is the expanded rail: "collapsed rail may not hide", and the two bands where a window
|
||||
size class rather than the visitor collapses one are given back below. */
|
||||
[data-navigation-rail][data-hide-when-collapsed] {
|
||||
@variant rail-collapsed {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* There is no room for a label under the 56px indicator at 80px, so a narrow rail is the icons
|
||||
alone. The label is taken out of the drawing, not out of the page: it is what names the
|
||||
destination for a screen reader. */
|
||||
[data-navigation-rail][data-width='narrow'] [data-navigation-rail-item] [data-navigation-label] {
|
||||
@variant rail-collapsed {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* A modal rail keeps its collapsed width in the layout while it is open over it, as Compose's
|
||||
ModalWideNavigationRail does; the adaptive rail does below `expanded`, and takes no room on a compact window. */
|
||||
[data-navigation-rail='modal'] {
|
||||
width: 6rem;
|
||||
width: var(--navigation-rail-collapsed-width);
|
||||
}
|
||||
|
||||
@media (width < 52.5rem) {
|
||||
[data-navigation-rail='adaptive'] {
|
||||
width: 6rem;
|
||||
width: var(--navigation-rail-collapsed-width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,10 +288,10 @@
|
||||
|
||||
/* "Compact → don't use a standard rail, use a navigation bar." A `collapsible` rail takes
|
||||
the width the visitor chose, which on a 360px screen would be two-thirds of it, so it
|
||||
is floored at the collapsed 96px. `collapsed` and `expanded` mean what they say and are
|
||||
is floored at its collapsed width. `collapsed` and `expanded` mean what they say and are
|
||||
left alone: wrap one in a `medium:` element if it must not show on a phone. */
|
||||
[data-navigation-rail='collapsible'] {
|
||||
width: 6rem;
|
||||
width: var(--navigation-rail-collapsed-width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,6 +395,72 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* A rail that hides when collapsed is in the same position: nothing is left in the layout for
|
||||
it to grow out of, so it takes the compact window's slide-in. The whole treatment is under
|
||||
the `rail-collapsed` variant, which stops the moment the rail is open — that is what makes
|
||||
it the expanded rail that comes back, on the rule above. */
|
||||
[data-navigation-rail][data-hide-when-collapsed] > [data-navigation-rail-panel] {
|
||||
@variant rail-collapsed {
|
||||
display: none;
|
||||
translate: -100% 0;
|
||||
transition:
|
||||
translate var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-easing-emphasized-accelerate),
|
||||
display var(--md-sys-motion-effects-default-duration) allow-discrete;
|
||||
|
||||
&:dir(rtl) {
|
||||
translate: 100% 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-navigation-rail][data-hide-when-collapsed][data-open] > [data-navigation-rail-panel] {
|
||||
display: flex;
|
||||
translate: 0 0;
|
||||
transition:
|
||||
translate var(--md-sys-motion-spatial-default-duration) var(--md-sys-motion-easing-emphasized-decelerate),
|
||||
display var(--md-sys-motion-spatial-default-duration) allow-discrete;
|
||||
|
||||
@starting-style {
|
||||
translate: -100% 0;
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
translate: 0 0;
|
||||
|
||||
@starting-style {
|
||||
translate: 100% 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The two bands the configuration does not reach, because there it is the window and not the
|
||||
visitor that collapses the rail: below `medium` for a collapsible rail — where a rail is
|
||||
held at its collapsed width anyway — and `medium` itself for the adaptive one, which M3
|
||||
gives a collapsed rail. (Below `medium` the adaptive rail is already away, over the page,
|
||||
and the block above it says so.) resources/js/navigation.js reads the same two numbers, so
|
||||
the menu button and the drawing agree at every width. */
|
||||
@media (width < 37.5rem) {
|
||||
[data-navigation-rail='collapsible'][data-hide-when-collapsed] {
|
||||
width: var(--navigation-rail-collapsed-width);
|
||||
}
|
||||
|
||||
[data-navigation-rail='collapsible'][data-hide-when-collapsed] > [data-navigation-rail-panel] {
|
||||
display: flex;
|
||||
translate: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (37.5rem <= width < 52.5rem) {
|
||||
[data-navigation-rail='adaptive'][data-hide-when-collapsed] {
|
||||
width: var(--navigation-rail-collapsed-width);
|
||||
}
|
||||
|
||||
[data-navigation-rail='adaptive'][data-hide-when-collapsed] > [data-navigation-rail-panel] {
|
||||
display: flex;
|
||||
translate: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-navigation-rail-scrim] {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -395,6 +523,16 @@
|
||||
padding-block: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
/* "Alignment: top or center; on tablets prefer center alignment for reach. Menu icon and FAB
|
||||
are always top-aligned" (docs/reference/m3/components-navigation-selection-inputs.md
|
||||
§ Navigation rail). Only the destinations move, then — the header and the footer stay at
|
||||
their ends of the rail — and `safe` puts them back at the top the moment there are more of
|
||||
them than fit, where centring would otherwise push the first one out of reach above the
|
||||
scroller. */
|
||||
[data-navigation-rail][data-align='center'] [data-navigation-rail-destinations] {
|
||||
justify-content: safe center;
|
||||
}
|
||||
|
||||
[data-navigation-rail-heading] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -513,13 +651,17 @@
|
||||
border-radius: var(--md-sys-shape-corner-full);
|
||||
}
|
||||
|
||||
/* The vertical layout — a compact bar, and a tall one at any width — fills the 56×32 box
|
||||
behind the icon; the horizontal layout fills the pill around icon and label instead, so it
|
||||
takes the fill off the box again. */
|
||||
[data-navigation-bar-item][data-active] [data-navigation-indicator] {
|
||||
background-image: linear-gradient(var(--md-sys-color-secondary-container), var(--md-sys-color-secondary-container));
|
||||
color: var(--md-sys-color-on-secondary-container);
|
||||
}
|
||||
|
||||
@container (width < 37.5rem) {
|
||||
[data-navigation-bar-item][data-active] [data-navigation-indicator] {
|
||||
background-image: linear-gradient(var(--md-sys-color-secondary-container), var(--md-sys-color-secondary-container));
|
||||
@container (width >= 37.5rem) {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item][data-active] [data-navigation-indicator] {
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,14 +721,16 @@
|
||||
background-color: var(--md-sys-color-on-secondary-container);
|
||||
}
|
||||
|
||||
@container (width < 37.5rem) {
|
||||
[data-navigation-bar-item] [data-navigation-pill]::before {
|
||||
display: none;
|
||||
}
|
||||
[data-navigation-bar-item] [data-navigation-pill]::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@container (width >= 37.5rem) {
|
||||
[data-navigation-bar-item] [data-navigation-indicator]::before {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item] [data-navigation-pill]::before {
|
||||
display: block;
|
||||
}
|
||||
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item] [data-navigation-indicator]::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -615,15 +759,17 @@
|
||||
}
|
||||
|
||||
/* M3's focus indicator, 3px of secondary 2px out, around the same shape. */
|
||||
@container (width < 37.5rem) {
|
||||
[data-navigation-bar-item]:focus-visible [data-navigation-indicator] {
|
||||
outline: 3px solid var(--md-sys-color-secondary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
[data-navigation-bar-item]:focus-visible [data-navigation-indicator] {
|
||||
outline: 3px solid var(--md-sys-color-secondary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@container (width >= 37.5rem) {
|
||||
[data-navigation-bar-item]:focus-visible [data-navigation-pill] {
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item]:focus-visible [data-navigation-indicator] {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
[data-navigation-bar]:not([data-tall]) [data-navigation-bar-item]:focus-visible [data-navigation-pill] {
|
||||
outline: 3px solid var(--md-sys-color-secondary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
@@ -646,6 +792,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* A bar that has slid off the bottom of the window is no longer there to clear, so the offset
|
||||
* everything pinned to the bottom reads drops to the bottom safe area and whatever the application
|
||||
* has docked on the bar — a `fab` button, the snackbar and the page's own bottom padding all follow
|
||||
* it down and come back up with the bar. Unlayered on purpose: <x-app-shell> publishes the variable
|
||||
* with a utility, and a rule in any layer loses to one. */
|
||||
[data-app-shell]:has([data-navigation-bar][data-hide-on-scroll][data-hidden]) {
|
||||
--material-bottom-bar: calc(var(--material-safe-bottom, env(safe-area-inset-bottom)) + var(--material-bottom-extra, 0px));
|
||||
}
|
||||
|
||||
/* A FAB in the rail's header rests at elevation 0, not the 3 a standalone FAB has
|
||||
* (docs/reference/m3/components-navigation-selection-inputs.md § Navigation rail: "when nested
|
||||
* within another component, such as the navigation rail, the FAB's resting elevation should be
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Apache-2.0).
|
||||
*
|
||||
* docked a 64px bar across the bottom of the screen in surface-container, square, its
|
||||
* controls spread out 4 to 32px apart between 16px ends
|
||||
* controls spread out 4 to 32px apart between 16px ends; with a FAB, the controls
|
||||
* at the start and the FAB flat at the end
|
||||
* floating a 64px pill in surface-container (or primary-container, `vibrant`) at elevation 3,
|
||||
* 8px at its ends and 4px between controls; `vertical` stands it on end
|
||||
*
|
||||
@@ -71,6 +72,24 @@
|
||||
color: var(--md-sys-color-primary);
|
||||
}
|
||||
|
||||
/* A docked toolbar with a FAB: the controls at the start, the FAB at the end, resting on the bar
|
||||
at elevation 0 — M3 puts a FAB nested in another component there, and Compose's
|
||||
FloatingActionButtonDefaults.bottomAppBarFabElevation() is 0 in every state. `<x-fab>` draws its
|
||||
shadow with utilities, which is one more reason this file is unlayered. */
|
||||
[data-toolbar][data-variant="docked"]:has(> [data-toolbar-fab]) {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
[data-toolbar-fab] {
|
||||
display: flex;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
[data-toolbar-fab] [data-fab],
|
||||
[data-toolbar-fab] [data-fab]:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* A floating toolbar and its FAB, side by side (or stacked, vertical). */
|
||||
[data-toolbar-group] {
|
||||
display: inline-flex;
|
||||
|
||||
Reference in New Issue
Block a user