feat: improve editor styling

pull/8104/head
NGPixel 2 weeks ago
parent 7b42568d8d
commit bf889ecfb2
No known key found for this signature in database

@ -284,16 +284,16 @@
<transition name="editor-markdown-preview">
<div class="editor-markdown-preview" v-if="state.previewShown">
<div class="editor-markdown-preview-toolbar">
<strong
<strong class="editor-markdown-preview-label"
><em>{{ t('editor.renderPreview') }}</em></strong
>
<w-separator class="ml-4 mr-2" vertical inset />
<w-separator class="ml-4 mr-2" vertical inset dark />
<w-btn
icon="mdi:arrow-vertical-lock"
padding="xs sm"
flat
@click="state.previewScrollSync = !state.previewScrollSync"
:color="state.previewScrollSync ? `primary` : null">
:color="state.previewScrollSync ? `secondary` : null">
<w-tooltip anchor="top middle" self="bottom middle">{{
t('editor.toggleScrollSync')
}}</w-tooltip>
@ -1920,6 +1920,7 @@ function notImplemented() {
<style lang="scss">
@use 'sass:color';
@use '@/css/_bricks.scss' as *;
$editor-height: calc(100vh - 64px - 96px);
$editor-preview-height: calc(100vh - 64px - 96px - 32px);
@ -1998,20 +1999,37 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
&-leave-to {
max-width: 0;
}
/*
Dark in either appearance, where this used to follow the theme. Two reasons, and the second is
the one that settled it: the bar labels the render rather than being part of it, so a dark strip
reads as a caption on the pane instead of as more page; and the brickwork at its right end needs
a colour with room above it -- `$grey-3` is 93% lightness, so a seam brick a shade lighter than
it is white, which is a hole rather than a highlight.
*/
&-toolbar {
color: $grey-8;
height: 32px;
display: flex;
align-items: center;
padding: 0 1rem;
background-color: $dark-2;
color: $grey-6;
@at-root .body--light & {
background-color: $grey-3;
}
@at-root .body--dark & {
background-color: $dark-2;
color: $grey-6;
}
/*
-> The right end steps out of the bar's grey into the editor orange, as brickwork -- the same
treatment the Visual editor's toolbar gets, and for the same reason: the actions rail is
orange while an editor is open, and this bar runs up to it. `css/_bricks.scss` has the
whole of how and why.
*/
@include brick-transition($dark-2);
}
/*
-> A shade lighter than the bar's own text colour, which the two icon buttons beside it still
take: this is the one piece of writing on the strip and what names the pane, so it carries
slightly more of the reader's attention than the controls do.
*/
&-label {
color: $grey-5;
}
&-content {
height: $editor-preview-height;

@ -320,7 +320,7 @@
}}</w-tooltip>
</w-btn>
<w-space />
<w-separator class="mx-1" vertical inset dark />
<w-btn dense icon="mdi:undo-variant" padding="xs sm" flat @click="runUndo">
<w-tooltip anchor="top middle" self="bottom middle">{{

@ -10,32 +10,50 @@
</w-chip>
</w-toolbar>
<div style="min-height: 450px">
<!-- -> Square: this one spans the dialog edge to edge, so a radius would cut across its corners -->
<!--
-> Square: this one spans the dialog edge to edge, so a radius would cut across its corners.
-> Dark in either appearance: what is typed here is the page's own code rather than a wiki
setting, and a dark field is both what an author reads code in and what tells their code
apart from the dialog around it.
-->
<util-code-editor
ref="editor"
v-model="state.content"
:language="language"
:min-height="450"
:aria-label="languageLabel"
square />
square
dark />
</div>
<w-card-actions class="card-actions">
<!--
Its own class rather than the shared `card-actions`: that one follows the app theme, and the
editor above is dark whichever theme is on -- a light bar under a dark pane reads as a different
component bolted to the bottom. Same treatment, and the same reasoning, as
`PageVersionSourceDialog`.
-->
<w-card-actions class="page-scripts-dialog-actions">
<w-space />
<!-- -> `grey-5`, not the `grey-7` a light bar takes: #757575 on this bar is barely there -->
<w-btn
class="acrylic-btn"
icon="la:times"
:label="t(`common.actions.discard`)"
color="grey-7"
color="grey-5"
padding="xs md"
flat
@click="$emit('close')" />
<!--
-> Apply, not Save: this writes the content into the page store and closes. Nothing reaches
the server until the page itself is saved, and a button saying otherwise invites an author
to close the editor believing their script is stored.
-->
<w-btn
icon="la:check"
:label="t(`common.actions.save`)"
:label="t(`common.actions.apply`)"
unelevated
color="primary"
padding="xs md"
@click="saveAndClose" />
@click="applyAndClose" />
</w-card-actions>
</w-card>
</template>
@ -133,7 +151,7 @@ function persist() {
EXPRESSION, and oxfmt reformats a semicolon-separated pair onto separate lines without the
semicolon, which stops being one. It broke the build twice while this file was being edited.
*/
function saveAndClose() {
function applyAndClose() {
persist()
emit('close')
}
@ -152,4 +170,13 @@ onMounted(() => {
})
</script>
<style lang="scss"></style>
<style lang="scss">
/* -> Colour only: WCardActions already lays the bar out */
.page-scripts-dialog-actions {
background-color: $dark-3;
background-image: radial-gradient(at top left, $dark-3, $dark-5);
border-top: 1px solid #000;
box-shadow: 0 -1px 0 0 rgba(#fff, 0.06);
color: #fff;
}
</style>

@ -11,7 +11,7 @@
-->
<div
class="util-code-editor"
:class="{ 'util-code-editor--square': square }"
:class="{ 'util-code-editor--square': square, 'util-code-editor--dark': dark }"
:style="{ height: `${minHeight}px`, '--util-code-editor-gutter': gutterWidth }">
<pre class="util-code-editor-view" aria-hidden="true"><code v-html="highlighted" /></pre>
<textarea
@ -88,6 +88,19 @@ const props = defineProps({
square: {
type: Boolean,
default: false
},
/**
* Draw dark whatever appearance the rest of the interface is in.
*
* For a field whose CONTENT is code rather than a setting that happens to be typed -- the page's
* own scripts and styles -- where a dark field is what the author is used to reading code in and
* what separates their code from the wiki's chrome around it. The editor otherwise follows
* `body--dark` like everything else, so this only ever adds the dark palette; there is no light
* counterpart, because nothing wants a light field on a dark page.
*/
dark: {
type: Boolean,
default: false
}
})
@ -435,22 +448,26 @@ defineExpose({
}
}
body.body--dark {
.util-code-editor {
border-color: rgb(255 255 255 / 0.3);
background-color: $dark-5;
background-image: linear-gradient(
to right,
$dark-4 0,
$dark-4 var(--util-code-editor-gutter),
rgb(255 255 255 / 0.12) var(--util-code-editor-gutter),
rgb(255 255 255 / 0.12) calc(var(--util-code-editor-gutter) + 1px),
transparent calc(var(--util-code-editor-gutter) + 1px)
);
&:focus-within {
border-color: var(--color-primary);
}
/*
The dark appearance, as a mixin because two different things ask for it: the interface being in
dark mode, and the `dark` prop asking for a dark field regardless. Everything is written relative
to the root element -- the layers and the gutter numbers are its descendants -- so that one body
serves both selectors below.
*/
@mixin util-code-editor-dark {
border-color: rgb(255 255 255 / 0.3);
background-color: $dark-5;
background-image: linear-gradient(
to right,
$dark-4 0,
$dark-4 var(--util-code-editor-gutter),
rgb(255 255 255 / 0.12) var(--util-code-editor-gutter),
rgb(255 255 255 / 0.12) calc(var(--util-code-editor-gutter) + 1px),
transparent calc(var(--util-code-editor-gutter) + 1px)
);
&:focus-within {
border-color: var(--color-primary);
}
.util-code-editor-view {
@ -465,54 +482,65 @@ body.body--dark {
caret-color: #e6edf3;
}
.util-code-editor {
.hljs-comment,
.hljs-quote {
color: #8b949e;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-doctag,
.hljs-formula {
color: #ff7b72;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #a5d6ff;
}
.hljs-number,
.hljs-variable,
.hljs-template-variable,
.hljs-symbol,
.hljs-bullet,
.hljs-attr,
.hljs-meta {
color: #79c0ff;
}
.hljs-title,
.hljs-section,
.hljs-selector-id,
.hljs-selector-class {
color: #d2a8ff;
}
.hljs-built_in,
.hljs-type,
.hljs-attribute,
.hljs-property,
.hljs-params {
color: #ffa657;
}
.hljs-name,
.hljs-tag {
color: #7ee787;
}
.hljs-deletion {
color: #ffa198;
}
.hljs-comment,
.hljs-quote {
color: #8b949e;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-doctag,
.hljs-formula {
color: #ff7b72;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #a5d6ff;
}
.hljs-number,
.hljs-variable,
.hljs-template-variable,
.hljs-symbol,
.hljs-bullet,
.hljs-attr,
.hljs-meta {
color: #79c0ff;
}
.hljs-title,
.hljs-section,
.hljs-selector-id,
.hljs-selector-class {
color: #d2a8ff;
}
.hljs-built_in,
.hljs-type,
.hljs-attribute,
.hljs-property,
.hljs-params {
color: #ffa657;
}
.hljs-name,
.hljs-tag {
color: #7ee787;
}
.hljs-deletion {
color: #ffa198;
}
}
body.body--dark .util-code-editor {
@include util-code-editor-dark;
}
/*
-> Both classes, not just the modifier: the light palette above is `.util-code-editor .hljs-*`, two
classes, so a single-class modifier would tie with it and be settled by which rule the bundler
emitted last. Naming the root twice puts this one ahead on specificity rather than on luck.
*/
.util-code-editor.util-code-editor--dark {
@include util-code-editor-dark;
}
</style>

@ -0,0 +1,116 @@
/*
The brick transition: a toolbar whose right end stops being its own colour and becomes the editor's
orange, through a staircase of brickwork rather than a straight edge.
Used by the Visual editor's toolbar and the Markdown editor's preview toolbar, which is the whole
reason this is a partial rather than a rule in either of them.
*/
/*
The Sass variables, loaded explicitly. `vite.config.js` injects `theme` and `palette` into every SFC
through `additionalData`, but that reaches only the files Vite itself hands to Sass -- a partial
pulled in by `@use` arrives without them. Same note as `_visual-editor.scss` and `_page-contents.scss`.
*/
@use 'sass:color';
@use 'palette' as *;
/**
* Lay the right end of a bar as brickwork stepping from `$cold` into `$hot`.
*
* Read the bar as five courses of brick. Counting in from the right edge, the last column has all
* five of its bricks knocked out, the one before it four, then three, then two, then one -- so the
* hot colour behind shows through as a staircase climbing to the right, the cold one thinning course
* by course until the corner is hot all the way up.
*
* Where the two meet, the brick on either side of the step is laid in a lighter shade of its own
* colour: one lighter cold and one lighter hot per course, each a single brick, so the seam reads as
* two diagonals of highlighted brick running with the step rather than as a hard edge. In every
* course the hot colour reaches column N and the cold one starts at N+1, which is what makes each of
* those runs a clean diagonal.
*
* Fifteen background layers rather than a clip path or an SVG, in three groups. The last five are the
* staircase: each one step, a hot rectangle anchored to the bottom-right corner, differing only in
* `background-size` -- one brick wide by five courses tall, two by four, three by three, four by two,
* five by one -- overlapping into the shape rather than tiling it, which is why they need no
* coordinates of their own. The first ten are the seam, one brick each, and those DO need
* coordinates: they are placed by offset from the right and top edges, a brick further along both per
* course. Earlier layers paint over later ones, so the seam is listed first.
*
* Courses are percentages, so there are five of them whatever the bar's height turns out to be. A
* brick's WIDTH is not a share of anything, so it comes from `$height` -- and a brick is square, which
* makes the staircase exactly as wide as the bar is tall, its diagonal a true 45 degrees, and the
* seam's offsets one and the same measure in both directions.
*
* `$deep-orange-9` is the default hot colour because that is what the actions rail turns while an
* editor is open (`PageActionsCol`): the rail and the bar are on screen together, at opposite edges of
* the same editor, so a second orange between them would read as a mistake rather than as a pair.
*
* The cold seam is emitted as a custom property, not baked into the layers, so that a bar whose own
* colour changes with the theme can restate just that one line in its dark branch instead of the
* whole stack. `$cold` is still required: it is what makes the default right for the common case, and
* an unset `var()` here would take the entire `background-image` down with it.
*
* @param {Color} $cold The bar's own background colour, which the brickwork steps out of.
* @param {Color} $hot The colour it steps into.
* @param {Length} $height The bar's height. Five courses are cut from it, and a brick is one square.
*/
@mixin brick-transition($cold, $hot: $deep-orange-9, $height: 32px) {
--brick: calc(#{$height} / 5);
--seam-cold: #{color.adjust($cold, $lightness: 10%)};
--seam-hot: #{color.adjust($hot, $lightness: 10%)};
background-image:
/* -> The seam, cold side: the last cold brick of each course */
linear-gradient(var(--seam-cold), var(--seam-cold)),
linear-gradient(var(--seam-cold), var(--seam-cold)),
linear-gradient(var(--seam-cold), var(--seam-cold)),
linear-gradient(var(--seam-cold), var(--seam-cold)),
linear-gradient(var(--seam-cold), var(--seam-cold)),
/* -> The seam, hot side: the first hot brick of each course */
linear-gradient(var(--seam-hot), var(--seam-hot)),
linear-gradient(var(--seam-hot), var(--seam-hot)),
linear-gradient(var(--seam-hot), var(--seam-hot)),
linear-gradient(var(--seam-hot), var(--seam-hot)),
linear-gradient(var(--seam-hot), var(--seam-hot)),
/* -> The staircase */
linear-gradient($hot, $hot),
linear-gradient($hot, $hot),
linear-gradient($hot, $hot),
linear-gradient($hot, $hot),
linear-gradient($hot, $hot);
background-repeat: no-repeat;
background-position:
/* -> Cold side: course 1 sits one brick in from the right, and each course one further */
right calc(var(--brick) * 1) top calc(var(--brick) * 0),
right calc(var(--brick) * 2) top calc(var(--brick) * 1),
right calc(var(--brick) * 3) top calc(var(--brick) * 2),
right calc(var(--brick) * 4) top calc(var(--brick) * 3),
right calc(var(--brick) * 5) top calc(var(--brick) * 4),
/* -> Hot side: the same diagonal, one column further right */
right calc(var(--brick) * 0) top calc(var(--brick) * 0),
right calc(var(--brick) * 1) top calc(var(--brick) * 1),
right calc(var(--brick) * 2) top calc(var(--brick) * 2),
right calc(var(--brick) * 3) top calc(var(--brick) * 3),
right calc(var(--brick) * 4) top calc(var(--brick) * 4),
right bottom,
right bottom,
right bottom,
right bottom,
right bottom;
background-size:
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) var(--brick),
var(--brick) 100%,
calc(var(--brick) * 2) 80%,
calc(var(--brick) * 3) 60%,
calc(var(--brick) * 4) 40%,
calc(var(--brick) * 5) 20%;
}

@ -18,6 +18,7 @@
@use 'sass:color';
@use 'palette' as *;
@use 'theme' as *;
@use 'bricks' as *;
/*
A flex child of the article column, filling whatever it is given rather than a height of its own.
@ -98,6 +99,12 @@
background-color: $primary;
color: #fff;
/*
-> The right end steps out of the bar's blue into the editor orange, as brickwork; the whole of
how and why is in `css/_bricks.scss`, which the Markdown editor's preview toolbar shares.
*/
@include brick-transition($primary);
/*
What a pressed button looks like here. The `primary` colour these used to take is the colour of
the bar itself, so an active button was drawn in the background it sits on and was simply

Loading…
Cancel
Save