fix: various UI improvements

pull/8104/head
NGPixel 4 days ago
parent 739cf9630b
commit a7330974a2
No known key found for this signature in database

@ -21,6 +21,7 @@
<w-btn <w-btn
class="aspect-square" class="aspect-square"
flat flat
square
icon="la:pen-nib" icon="la:pen-nib"
:color="editorStore.isActive ? `white` : `deep-orange-9`" :color="editorStore.isActive ? `white` : `deep-orange-9`"
:disable="isRedirect" :disable="isRedirect"
@ -32,6 +33,7 @@
class="h-12" class="h-12"
v-if="flagsStore.experimental" v-if="flagsStore.experimental"
flat flat
square
icon="la:project-diagram" icon="la:project-diagram"
:color="editorStore.isActive ? `white` : `deep-orange-9`" :color="editorStore.isActive ? `white` : `deep-orange-9`"
aria-label="Page Data" aria-label="Page Data"
@ -44,6 +46,7 @@
class="h-12" class="h-12"
v-if="editorStore.isActive && !isRedirect" v-if="editorStore.isActive && !isRedirect"
flat flat
square
color="white" color="white"
:text-color="hasPendingAssets ? `white` : `deep-orange-3`" :text-color="hasPendingAssets ? `white` : `deep-orange-3`"
aria-label="Pending Asset Uploads"> aria-label="Pending Asset Uploads">
@ -112,6 +115,7 @@
class="h-12" class="h-12"
v-if="showHistory" v-if="showHistory"
flat flat
square
icon="la:history" icon="la:history"
:color="editorStore.isActive ? `white` : `grey`" :color="editorStore.isActive ? `white` : `grey`"
aria-label="Page History" aria-label="Page History"
@ -126,6 +130,7 @@
class="h-12" class="h-12"
v-if="showSource" v-if="showSource"
flat flat
square
icon="la:code" icon="la:code"
:color="editorStore.isActive ? `white` : `grey`" :color="editorStore.isActive ? `white` : `grey`"
aria-label="Page Source" aria-label="Page Source"
@ -140,6 +145,7 @@
<w-btn <w-btn
class="h-12" class="h-12"
flat flat
square
icon="la:ellipsis-h" icon="la:ellipsis-h"
:color="editorStore.isActive ? `deep-orange-2` : `grey`" :color="editorStore.isActive ? `deep-orange-2` : `grey`"
aria-label="Page Actions"> aria-label="Page Actions">
@ -201,6 +207,7 @@
class="h-12" class="h-12"
v-if="userStore.can(`write:pages`)" v-if="userStore.can(`write:pages`)"
flat flat
square
icon="la:copy" icon="la:copy"
:color="editorStore.isActive ? `deep-orange-2` : `grey`" :color="editorStore.isActive ? `deep-orange-2` : `grey`"
aria-label="Duplicate Page" aria-label="Duplicate Page"
@ -211,6 +218,7 @@
class="h-12" class="h-12"
v-if="userStore.can(`manage:pages`)" v-if="userStore.can(`manage:pages`)"
flat flat
square
icon="la:share" icon="la:share"
:color="editorStore.isActive ? `deep-orange-2` : `grey`" :color="editorStore.isActive ? `deep-orange-2` : `grey`"
aria-label="Rename / Move Page" aria-label="Rename / Move Page"
@ -221,6 +229,7 @@
class="h-12" class="h-12"
v-if="userStore.can(`delete:pages`)" v-if="userStore.can(`delete:pages`)"
flat flat
square
icon="la:trash" icon="la:trash"
:color="editorStore.isActive ? `deep-orange-2` : `grey`" :color="editorStore.isActive ? `deep-orange-2` : `grey`"
aria-label="Delete Page" aria-label="Delete Page"

@ -228,6 +228,22 @@ function onKeydown(ev) {
border-bottom: 1px solid var(--page-chrome-rule); border-bottom: 1px solid var(--page-chrome-rule);
} }
/*
The corner, through the 1px gap above. The article column's right-hand rule starts under that gap
and this line ends over it, so the one pixel where they turn the corner was left undrawn -- a
notch in what is meant to read as a single line. Only over the article column: the editor takes
its place under the strip too, and has no rule to join.
*/
&:has(+ .page-article-col)::before {
content: '';
position: absolute;
right: 0;
bottom: -1px;
width: 1px;
height: 1px;
background-color: var(--page-chrome-rule);
}
@at-root .body--light & { @at-root .body--light & {
background-color: $grey-2; background-color: $grey-2;
} }

@ -83,6 +83,11 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false default: false
}, },
/** No border radius, for a button that fills a bar edge to edge. */
square: {
type: Boolean,
default: false
},
/** Solid background, no shadow. */ /** Solid background, no shadow. */
unelevated: { unelevated: {
type: Boolean, type: Boolean,
@ -211,7 +216,7 @@ const isSolid = computed(() => !props.flat && !props.outline)
Geometry taken from Quasar's own button variables, so migrated screens keep their exact metrics: Geometry taken from Quasar's own button variables, so migrated screens keep their exact metrics:
font-size 14px (text-sm) · line-height 1.715em · padding 4px 16px · dense padding .285em font-size 14px (text-sm) · line-height 1.715em · padding 4px 16px · dense padding .285em
min-height 2.572em (2em dense) · round 3em (2.4em dense) with no padding min-height 2.572em (2em dense) · round 3em (2.4em dense) with no padding
border-radius 3px, 28px when `rounded`, 50% when `round` border-radius 3px, 28px when `rounded`, 50% when `round`, none when `square`
Sizes are em-relative so they track the font size instead of being re-derived per variant. Sizes are em-relative so they track the font size instead of being re-derived per variant.
Padding and min-height live in `styles` below, since they are em values rather than scale steps. Padding and min-height live in `styles` below, since they are em values rather than scale steps.
*/ */
@ -222,6 +227,10 @@ const classes = computed(() => [
? 'rounded-full' ? 'rounded-full'
: props.rounded : props.rounded
? 'rounded-[28px]' ? 'rounded-[28px]'
: props.square
? props.push
? 'w-push rounded-none'
: 'rounded-none'
: props.push : props.push
? 'w-push rounded-[7px]' ? 'w-push rounded-[7px]'
: 'rounded-[3px]', : 'rounded-[3px]',

@ -50,11 +50,8 @@ import { computed } from 'vue'
* The track is a recessed channel and the knob sits proud of it, lit from the top left; state is * The track is a recessed channel and the knob sits proud of it, lit from the top left; state is
* read from where the knob sits plus the status tone -- a tick when on, a cross when off. * read from where the knob sits plus the status tone -- a tick when on, a cross when off.
* *
* WHICH part wears that tone depends on the theme, and it is the one thing about this control that * The pin itself wears that tone, with a white mark on it, in both themes -- which is what gives the
* is not just a change of value between the two. On a dark surface the pin itself is the coloured * switch something bright to read at a glance.
* element, with a white mark on it, which is what gives the switch something bright to read at a
* glance. On a light one the pin stays near-white and the MARK carries the colour, because a
* saturated pin on a pale page shouts far louder than the same pin does against a dark one.
* *
* A disabled switch drops both its relief and its status colour: no glow, a neutral pin, a grey * A disabled switch drops both its relief and its status colour: no glow, a neutral pin, a grey
* mark, and a flat track. Dimming alone read as "slightly faded" rather than "not available", and a * mark, and a flat track. Dimming alone read as "slightly faded" rather than "not available", and a
@ -154,29 +151,32 @@ function toggle() {
*/ */
.w-toggle { .w-toggle {
/* /*
The status tone, in one place: the glow always takes it, and each theme points either the pin or The status tone, in one place: the glow and the pin both take it. It lives on the button rather
the mark at it further down. It lives on the button rather than on the three elements that draw than on the elements that draw it, so they cannot disagree about which state they are showing.
it, so they cannot disagree about which state they are showing.
*/ */
--w-toggle-status: var(--color-negative); --w-toggle-status: var(--color-negative);
--w-toggle-track: #dfe3ea; --w-toggle-track: #dfe3ea;
--w-toggle-rim: #ffffff; --w-toggle-rim: #ffffff;
--w-toggle-knob: #fdfdfe; --w-toggle-knob: var(--w-toggle-status);
--w-toggle-knob-rim: rgb(0 0 0 / 0.06); --w-toggle-knob-rim: rgb(0 0 0 / 0.06);
/* A white ring inside the pin's edge, setting the saturated tone off from the track */
--w-toggle-knob-border: rgb(255 255 255 / 0.5);
/*
The pin's up-left shadow is dark here too: a white one sits against the track's white rim and
gives the pin no edge on that side. Lighter than the down-right one, so the light still reads as
coming from the top left.
*/
--w-toggle-knob-highlight: rgb(0 0 0 / 0.06);
--w-toggle-shadow: rgb(0 0 0 / 0.18); --w-toggle-shadow: rgb(0 0 0 / 0.18);
--w-toggle-highlight: rgb(255 255 255 / 0.95); --w-toggle-highlight: rgb(255 255 255 / 0.95);
--w-toggle-cast: rgb(0 0 0 / 0.12); --w-toggle-cast: rgb(0 0 0 / 0.12);
--w-toggle-glow: 0.62; --w-toggle-glow: 0.62;
--w-toggle-mark: var(--w-toggle-status); --w-toggle-mark: #ffffff;
--w-toggle-mark-disabled: #8a8f98; --w-toggle-mark-disabled: #8a8f98;
/* /* White on the positive tone is a weak pairing, so the mark gets an edge to read against */
A pale pin needs no relief under the mark, so the copy is the pin's own tone and draws nothing. --w-toggle-mark-shadow: rgb(0 0 0 / 0.3);
Keeping it painted rather than hidden is what makes the dark theme a change of colour and not a /* Shed the status colour when the control cannot be moved */
change of markup. --w-toggle-knob-disabled: #fdfdfe;
*/
--w-toggle-mark-shadow: #ffffff;
/* The pin is neutral here either way, so disabled changes only the mark */
--w-toggle-knob-disabled: var(--w-toggle-knob);
} }
.w-toggle[aria-checked='true'] { .w-toggle[aria-checked='true'] {
@ -186,18 +186,16 @@ function toggle() {
:global(body.body--dark .w-toggle) { :global(body.body--dark .w-toggle) {
--w-toggle-track: #262c38; --w-toggle-track: #262c38;
--w-toggle-rim: #39414f; --w-toggle-rim: #39414f;
--w-toggle-knob: var(--w-toggle-status);
--w-toggle-knob-rim: rgb(255 255 255 / 0.1); --w-toggle-knob-rim: rgb(255 255 255 / 0.1);
/* Softer than the light theme's, which stands out more against the dark track */
--w-toggle-knob-border: rgb(255 255 255 / 0.25);
--w-toggle-knob-highlight: var(--w-toggle-highlight);
--w-toggle-shadow: rgb(0 0 0 / 0.6); --w-toggle-shadow: rgb(0 0 0 / 0.6);
--w-toggle-highlight: rgb(255 255 255 / 0.07); --w-toggle-highlight: rgb(255 255 255 / 0.07);
--w-toggle-cast: rgb(0 0 0 / 0.45); --w-toggle-cast: rgb(0 0 0 / 0.45);
/* Held up a little: the same tone has less to carry against a dark channel than a pale one */ /* Held up a little: the same tone has less to carry against a dark channel than a pale one */
--w-toggle-glow: 0.72; --w-toggle-glow: 0.72;
--w-toggle-mark: #ffffff;
--w-toggle-mark-disabled: #aeb4bf; --w-toggle-mark-disabled: #aeb4bf;
/* White on the positive tone is a weak pairing, so the mark gets an edge to read against */
--w-toggle-mark-shadow: rgb(0 0 0 / 0.3);
/* Shed the status colour when the control cannot be moved; this is the pin's old neutral */
--w-toggle-knob-disabled: #6b7382; --w-toggle-knob-disabled: #6b7382;
} }
@ -238,16 +236,17 @@ function toggle() {
} }
/* /*
The transition is declared here rather than with Tailwind's `transition-transform`, because in the The transition is declared here rather than with Tailwind's `transition-transform`, because the
dark theme the pin's colour has to travel with its movement. Same duration and easing as the glow, pin's colour has to travel with its movement. Same duration and easing as the glow,
which shares the knob's offset class and so must move in step with it. which shares the knob's offset class and so must move in step with it.
*/ */
.w-toggle__knob { .w-toggle__knob {
background-color: var(--w-toggle-knob); background-color: var(--w-toggle-knob);
box-shadow: box-shadow:
inset 0 0 0 2px var(--w-toggle-knob-border),
0 0 0 1px var(--w-toggle-knob-rim), 0 0 0 1px var(--w-toggle-knob-rim),
2px 2px 4px var(--w-toggle-shadow), 2px 2px 4px var(--w-toggle-shadow),
-2px -2px 4px var(--w-toggle-highlight); -2px -2px 4px var(--w-toggle-knob-highlight);
transition: transition:
translate 0.2s var(--ease-standard), translate 0.2s var(--ease-standard),
background-color 0.2s var(--ease-standard); background-color 0.2s var(--ease-standard);
@ -272,8 +271,7 @@ function toggle() {
The mark's drop shadow. Both copies are positioned, so painting order is document order and the The mark's drop shadow. Both copies are positioned, so painting order is document order and the
real mark covers this one -- `z-index: -1` would put it behind the knob's own background instead. real mark covers this one -- `z-index: -1` would put it behind the knob's own background instead.
What it draws is the theme's business, and both answers are in the variable blocks above: a pixel A pixel of dark under the white mark, so it keeps an edge against the coloured pin.
of dark under a white mark on a coloured pin, or nothing at all on a pale one.
*/ */
.w-toggle__mark-shadow { .w-toggle__mark-shadow {
position: absolute; position: absolute;
@ -295,7 +293,9 @@ function toggle() {
.w-toggle--disabled .w-toggle__knob { .w-toggle--disabled .w-toggle__knob {
background-color: var(--w-toggle-knob-disabled); background-color: var(--w-toggle-knob-disabled);
box-shadow: 0 0 0 1px var(--w-toggle-knob-rim); box-shadow:
inset 0 0 0 2px var(--w-toggle-knob-border),
0 0 0 1px var(--w-toggle-knob-rim);
} }
.w-toggle--disabled .w-toggle__track { .w-toggle--disabled .w-toggle__track {

@ -27,6 +27,40 @@
@use 'palette' as *; @use 'palette' as *;
@use 'theme' as *; @use 'theme' as *;
/*
One edge of a box drawn as a hairline -- one DEVICE pixel, like `.w-hairline` -- in the colour held
by the custom property `$color-var`. `$pseudo` is which of the box's two pseudo-elements draws it,
so a box can have a hairline on both edges.
The border is kept, transparent, so the box is exactly the size it was with a real one, and the line
is painted over that border area by a pseudo-element scaled by `1 / --w-dpr`. The scale runs towards
the box's OUTER edge, so two of these meeting at a seam -- a bar's underline and the top edge of the
bar below it -- sit against each other with nothing between them. What the scale leaves bare shows
the box's own background, which is why the gradients using this are `border-box`: under the default
`padding-box` origin a gradient repeats into the border area, and its first stop shows as a sliver.
Screen only: the print stylesheet redraws these edges as borders of its own.
*/
@mixin hairline-edge($side, $color-var, $pseudo: after) {
position: relative;
border-#{$side}: 1px solid transparent;
@media screen {
&::#{$pseudo} {
content: '';
position: absolute;
left: 0;
right: 0;
#{$side}: -1px;
height: 1px;
background-color: var(#{$color-var});
transform: scaleY(calc(1 / var(--w-dpr, 1)));
transform-origin: #{$side} left;
pointer-events: none;
}
}
}
/* /*
Where the contents column stops being able to afford 300px. A page view's own threshold, not one of the Where the contents column stops being able to afford 300px. A page view's own threshold, not one of the
app's -- `_palette.scss` is for the breakpoints the whole app shares, and this one is a function of a app's -- `_palette.scss` is for the breakpoints the whole app shares, and this one is a function of a
@ -80,10 +114,17 @@ $toc-overlay-max: 749.98px;
opacity: 0.12; opacity: 0.12;
} }
/*
The pair of lines between this bar and the page header under it -- this bar's underline, then the
header's top edge -- are hairlines drawn by `hairline-edge` rather than borders: a 1px border is 1.5
or 2 device pixels on a scaled display, twice the weight of every `.w-hairline` rule around it.
*/
.page-breadcrumbs { .page-breadcrumbs {
@include hairline-edge(bottom, --page-breadcrumbs-rule);
@at-root .body--light & { @at-root .body--light & {
background: linear-gradient(to bottom, $grey-1 0%, $grey-3 100%); background: linear-gradient(to bottom, $grey-1 0%, $grey-3 100%) border-box;
border-bottom: 1px solid $grey-4; --page-breadcrumbs-rule: #{$grey-4};
} }
/* /*
The bar sets a background per theme, so it owes a foreground too: the LAST crumb -- the current The bar sets a background per theme, so it owes a foreground too: the LAST crumb -- the current
@ -94,8 +135,8 @@ $toc-overlay-max: 749.98px;
color: var(--color-black); color: var(--color-black);
} }
@at-root .body--dark & { @at-root .body--dark & {
background: linear-gradient(to bottom, $dark-3 0%, $dark-4 100%); background: linear-gradient(to bottom, $dark-3 0%, $dark-4 100%) border-box;
border-bottom: 1px solid $dark-3; --page-breadcrumbs-rule: #{$dark-3};
color: var(--color-white); color: var(--color-white);
} }
@ -134,15 +175,17 @@ $toc-overlay-max: 749.98px;
*/ */
.page-breadcrumbs--version { .page-breadcrumbs--version {
@at-root .body--light & { @at-root .body--light & {
background: linear-gradient(to bottom, var(--color-indigo-6) 0%, var(--color-indigo-7) 100%); background: linear-gradient(to bottom, var(--color-indigo-6) 0%, var(--color-indigo-7) 100%)
border-bottom: 1px solid var(--color-indigo-9); border-box;
--page-breadcrumbs-rule: var(--color-indigo-9);
color: #fff; color: #fff;
} }
/* -> A step down the same ramp, so the band reads as deliberate against a dark page rather than as /* -> A step down the same ramp, so the band reads as deliberate against a dark page rather than as
the light theme's bar left switched on */ the light theme's bar left switched on */
@at-root .body--dark & { @at-root .body--dark & {
background: linear-gradient(to bottom, var(--color-indigo-7) 0%, var(--color-indigo-9) 100%); background: linear-gradient(to bottom, var(--color-indigo-7) 0%, var(--color-indigo-9) 100%)
border-bottom: 1px solid var(--color-indigo-9); border-box;
--page-breadcrumbs-rule: var(--color-indigo-9);
color: #fff; color: #fff;
} }
} }
@ -185,6 +228,8 @@ $toc-overlay-max: 749.98px;
.page-header { .page-header {
height: 95px; height: 95px;
@include hairline-edge(top, --page-header-rule);
@include hairline-edge(bottom, --page-header-underline, before);
/* /*
Sized by its contents on a phone instead, which comes out around 70px: the 95px is pitched for a 64px Sized by its contents on a phone instead, which comes out around 70px: the 95px is pitched for a 64px
@ -200,14 +245,15 @@ $toc-overlay-max: 749.98px;
} }
@at-root .body--light & { @at-root .body--light & {
background: linear-gradient(to bottom, $grey-2 0%, $grey-1 100%); background: linear-gradient(to bottom, $grey-2 0%, $grey-1 100%) border-box;
border-bottom: 1px solid $grey-4; --page-header-rule: #fff;
border-top: 1px solid #fff; --page-header-underline: #{$grey-4};
} }
@at-root .body--dark & { @at-root .body--dark & {
background: linear-gradient(to bottom, $dark-4 0%, $dark-3 100%); background: linear-gradient(to bottom, $dark-4 0%, $dark-3 100%) border-box;
// border-bottom: 1px solid $dark-5; // -> No underline in the dark theme, and no border holding a place for one either
border-top: 1px solid $dark-6; border-bottom: 0;
--page-header-rule: #{$dark-6};
} }
.no-height .q-field__control { .no-height .q-field__control {
@ -396,14 +442,26 @@ $toc-overlay-max: 749.98px;
background-color: $dark-5; background-color: $dark-5;
} }
// A light rule on the light sidebar, near-black on the dark one -- it reads as the bevel between /*
// two panels rather than as a drawn line. A bevel between two panels rather than a drawn line: a grey hairline over a light one on the light
// sidebar, over a near-black one on the dark. The grey is `--page-chrome-rule`, the article column's
// The original set a background-colour here as well as a border. It never showed: the element is right-hand edge beside it -- `$grey-4` light, the lighter dark grey dark -- and the second line is
// 1px tall with `box-sizing: border-box`, so the content box is 0px and the opaque border covers this separator's own `--w-hairline-color`.
// it completely. Only the border colour is carried across.
One separator drawn two lines tall rather than two separators, so the hairline transform -- which
scales from the top -- thins both halves alike and they stay touching.
*/
.w-separator { .w-separator {
--w-hairline-color: #fff; --w-hairline-color: #fff;
height: 2px;
&::after {
background: linear-gradient(
to bottom,
var(--page-chrome-rule) 50%,
var(--w-hairline-color) 50%
);
}
} }
@at-root .body--dark & .w-separator { @at-root .body--dark & .w-separator {
--w-hairline-color: #070a0d; --w-hairline-color: #070a0d;

@ -767,11 +767,18 @@
); );
} }
/* The wider, fainter band trailing below the heading */ /*
The wider, fainter band trailing below the heading.
Under the heading's own box come three hairlines -- a gap, the line in `::after` below, a gap --
and then this band. All three are one DEVICE pixel, so the band starts three of them down rather
than 3px: a whole CSS pixel of gap either side of a one-device-pixel line reads as the line
floating in a white stripe on a scaled display.
*/
.w-section-header::before { .w-section-header::before {
content: ''; content: '';
position: absolute; position: absolute;
bottom: -13px; top: calc(100% + 3px / var(--w-dpr, 1));
left: 0; left: 0;
z-index: 0; z-index: 0;
width: 100%; width: 100%;
@ -784,7 +791,7 @@
} }
/* /*
The hairline immediately under it, fading out towards the right. The hairline under it, one device pixel clear of it, fading out towards the right.
Scaled by `1 / --w-dpr` like `.w-hairline`, so it paints exactly one DEVICE pixel: a 1px CSS Scaled by `1 / --w-dpr` like `.w-hairline`, so it paints exactly one DEVICE pixel: a 1px CSS
rule is 1.5 device pixels at 150% display scaling, which reads visibly heavier than the same rule is 1.5 device pixels at 150% display scaling, which reads visibly heavier than the same
@ -793,7 +800,7 @@
.w-section-header::after { .w-section-header::after {
content: ''; content: '';
position: absolute; position: absolute;
bottom: -2px; top: calc(100% + 1px / var(--w-dpr, 1));
left: 0; left: 0;
z-index: 0; z-index: 0;
width: 100%; width: 100%;
@ -804,7 +811,7 @@
color-mix(in srgb, var(--color-primary) 25%, transparent) color-mix(in srgb, var(--color-primary) 25%, transparent)
); );
transform: scaleY(calc(1 / var(--w-dpr, 1))); transform: scaleY(calc(1 / var(--w-dpr, 1)));
transform-origin: bottom; transform-origin: top;
} }
body.body--dark .w-section-header { body.body--dark .w-section-header {

@ -58,19 +58,21 @@
class="flex-1 px-2" class="flex-1 px-2"
flat flat
dense dense
square
icon="la:globe" icon="la:globe"
:label="siteStore.localeAlias(commonStore.locale)" :label="siteStore.localeAlias(commonStore.locale)"
:aria-label="siteStore.localeAlias(commonStore.locale)" :aria-label="siteStore.localeAlias(commonStore.locale)"
size="sm"> size="sm">
<locale-selector-menu :offset="[-5, 5]" /> <locale-selector-menu :offset="[-5, 5]" />
</w-btn> </w-btn>
<w-separator v-if="canBrowse" vertical /> <w-separator v-if="canBrowse" class="sidebar-actions-sep" vertical />
</template> </template>
<w-btn <w-btn
v-if="canBrowse" v-if="canBrowse"
class="flex-1 px-2" class="flex-1 px-2"
flat flat
dense dense
square
icon="la:sitemap" icon="la:sitemap"
:label="t(`common.sidebar.browse`)" :label="t(`common.sidebar.browse`)"
:aria-label="t(`common.sidebar.browse`)" :aria-label="t(`common.sidebar.browse`)"
@ -391,16 +393,58 @@ function openSidebar() {
</script> </script>
<style lang="scss"> <style lang="scss">
/*
The bar's bottom edge is a dark line over a light one, drawn here as a hairline pair rather than as
this bar's border plus `.sidebar-nav`'s top border: a border stays a whole CSS pixel on a scaled
display, which put it at twice the weight of the separator between the buttons. The light half has
to be drawn from this side because the nav below is its own scroll container, and anything it
drew would scroll away with the list. 39px and the 2px of padding are the old 38px bar plus the
nav's 1px border, so neither the buttons nor the list move. The pair is thinned towards the TOP of
that padding, so on a scaled display the separator still runs down into it without a gap.
*/
.sidebar-actions { .sidebar-actions {
position: relative;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0.05) 100%); background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0.05) 100%);
border-bottom: 1px solid rgba(0, 0, 0, 0.2); height: 39px;
height: 38px; padding-bottom: 2px;
&::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 2px;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.2) 50%, rgba(255, 255, 255, 0.15) 50%);
transform: scaleY(calc(1 / var(--w-dpr, 1)));
transform-origin: top left;
pointer-events: none;
}
// -> The nav keeps its own light top border for when there is no bar above it, under the header
+ .sidebar-nav {
border-top: none;
}
// -> Where the two buttons above get their colour, so neither carries a `color` prop: `WBtn` emits // -> Where the two buttons above get their colour, so neither carries a `color` prop: `WBtn` emits
// an inline `color`, which would outrank this rule // an inline `color`, which would outrank this rule
.w-btn { .w-btn {
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
} }
/*
The rule between the two buttons is the same dark-then-light hairline pair as the bar's bottom
edge, drawn as ONE separator two lines wide so the hairline transform thins both halves alike.
Colours are pinned rather than left to `--w-hairline-color`, which flips to white in dark mode,
and the sidebar is dark in both.
*/
.sidebar-actions-sep {
width: 2px;
&::after {
background: linear-gradient(to right, rgba(0, 0, 0, 0.2) 50%, rgba(255, 255, 255, 0.15) 50%);
}
}
} }
.sidebar-mini { .sidebar-mini {

@ -57,7 +57,9 @@
<span>{{ t(`admin.editors.` + editor.id + `Description`) }}</span> <span>{{ t(`admin.editors.` + editor.id + `Description`) }}</span>
</w-item-label> </w-item-label>
<w-item-label caption v-if="editor.useRendering"> <w-item-label caption v-if="editor.useRendering">
<em class="text-purple">{{ t('admin.editors.useRenderingPipeline') }}</em> <em class="text-purple dark:text-purple-3">{{
t('admin.editors.useRenderingPipeline')
}}</em>
</w-item-label> </w-item-label>
</w-item-section> </w-item-section>
<template v-if="editor.hasConfig"> <template v-if="editor.hasConfig">

@ -125,9 +125,10 @@
</w-card-header> </w-card-header>
<template v-for="(lc, idx) of orderedLocales" :key="lc.code"> <template v-for="(lc, idx) of orderedLocales" :key="lc.code">
<w-separator v-if="idx === dividerIndex" class="my-2" inset /> <w-separator v-if="idx === dividerIndex" class="my-2" inset />
<!-- -> Only an installed row is a label: there is no toggle behind the Install button <!-- -> Not a label, unlike the toggles above: a label forwards a click to its FIRST
for the click to be forwarded to --> labelable descendant, and a button is one, so the whole row opened the alias
<w-item :tag="lc.isInstalled ? `label` : null"> editor rather than flipping the toggle -->
<w-item>
<blueprint-icon :text="lc.language" /> <blueprint-icon :text="lc.language" />
<w-item-section> <w-item-section>
<w-item-label>{{ lc.name }}</w-item-label> <w-item-label>{{ lc.name }}</w-item-label>

Loading…
Cancel
Save