440 lines
9.9 KiB
SCSS
440 lines
9.9 KiB
SCSS
// Page chrome for the standalone (non-editor) pages: the login page, the setup
|
|
// wizard and the Space Manager. Everything here is scoped under
|
|
// `body.sb-standalone` so this partial can share one stylesheet with the plug
|
|
// panels, which are the same components without the page furniture.
|
|
@use "tokens";
|
|
|
|
body.sb-standalone {
|
|
--top-color: #66a3bb;
|
|
--bottom-color: #0b3a69;
|
|
min-height: 100vh;
|
|
background: linear-gradient(to bottom, var(--top-color), var(--bottom-color));
|
|
font-family: var(--ui-font);
|
|
|
|
// The gradient lives on <body> rather than <html> so it can be scoped by a
|
|
// class; <html> only needs to stop collapsing under it.
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 1.5rem;
|
|
font-weight: normal;
|
|
}
|
|
|
|
// Subsection heading (e.g. "Role", "Password", "API tokens" on the user
|
|
// detail screen) — a clear step below the page title, not level with it.
|
|
h2 {
|
|
font-size: 1.15rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 1.05rem;
|
|
font-weight: 600;
|
|
margin-top: 1.5rem;
|
|
}
|
|
|
|
// Vertical rhythm between subsections (the user detail screen's
|
|
// Role/Password/API tokens blocks) so they read as distinct groups.
|
|
section {
|
|
margin-top: 1.75rem;
|
|
}
|
|
|
|
details {
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
// Every list on these pages is a custom `list-style: none` layout, never
|
|
// prose, so the UA's ~40px marker indent is pure dead space on the left.
|
|
// The old bundle got this from a global `* { padding: 0 }` reset; that reset
|
|
// is gone (it broke the editor), so state it where it actually applies.
|
|
ul,
|
|
ol {
|
|
padding-left: 0;
|
|
}
|
|
|
|
// The component library has no Radio, so the setup wizard's hosting choice
|
|
// uses native radios — just tint them to match .sb-checkbox.
|
|
input[type="radio"] {
|
|
accent-color: var(--ui-accent-color);
|
|
cursor: pointer;
|
|
}
|
|
|
|
form label {
|
|
display: block;
|
|
margin-top: 0.75rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
// Full-width fields inside forms; the components supply everything else.
|
|
form .sb-input,
|
|
form .sb-select {
|
|
width: 100%;
|
|
}
|
|
|
|
.row {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-top: 1rem;
|
|
align-items: center;
|
|
}
|
|
|
|
// Links used for navigation should look like their button counterparts while
|
|
// retaining normal link behaviour (copy, open in a new tab, history).
|
|
a.sb-button,
|
|
a.sb-button-primary {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-decoration: none;
|
|
}
|
|
|
|
a.sb-tab {
|
|
text-decoration: none;
|
|
}
|
|
|
|
.sb-help-text {
|
|
display: block;
|
|
margin-top: 0.25rem;
|
|
font-weight: normal;
|
|
color: var(--subtle-color);
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
.sb-spaces-error {
|
|
color: #b00020;
|
|
}
|
|
|
|
.sb-spaces-ok {
|
|
color: #1a7f37;
|
|
|
|
// Live regions are rendered before they have anything to announce; an
|
|
// empty one must not claim a flex gap next to the buttons it follows.
|
|
&:empty {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.sb-spaces-warn {
|
|
color: #9a6700;
|
|
}
|
|
}
|
|
|
|
// The 720px reading column, used by the setup wizard and the Space Manager.
|
|
// The login page opts out: it centres a card in the viewport instead.
|
|
body.sb-standalone.sb-page {
|
|
max-width: 720px;
|
|
margin: 0 auto;
|
|
padding: 2rem 1rem;
|
|
}
|
|
|
|
// The "floating island" card. On the column pages (Space Manager, setup
|
|
// wizard) the mount point *is* the card; the login page mounts a full-viewport
|
|
// centring wrapper instead and puts .floating-island inside it, so `#root` is
|
|
// qualified with .sb-page to avoid drawing a second card around the first.
|
|
.sb-standalone .floating-island,
|
|
.sb-standalone.sb-page #root {
|
|
padding: 3em;
|
|
border-radius: 8px;
|
|
box-shadow: rgba(0, 0, 0, 0.35) 0px 20px 20px;
|
|
background-color: white;
|
|
border: var(--modal-border-color) 1px solid;
|
|
margin-block: 2rem;
|
|
}
|
|
|
|
.sb-standalone .center {
|
|
height: 100vh;
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
|
|
.sb-standalone .flow {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.sb-standalone .flow>* {
|
|
margin-block: 0;
|
|
}
|
|
|
|
.sb-standalone .flow>*+* {
|
|
margin-block-start: var(--space, 1rem);
|
|
}
|
|
|
|
.sb-standalone .error-message {
|
|
color: #b00020;
|
|
}
|
|
|
|
.sb-standalone .checkbox-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
flex-direction: row;
|
|
}
|
|
|
|
// A checkbox's label sits beside it, not above it: undo the block layout,
|
|
// top margin and bold weight that `form label` gives the field labels, which
|
|
// otherwise push the text out of line with the box and make every option
|
|
// shout.
|
|
.sb-standalone .checkbox-wrapper label,
|
|
.sb-standalone form .checkbox-wrapper label {
|
|
display: inline;
|
|
margin: 0;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.sb-standalone .password-field {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.sb-standalone .password-field .sb-input {
|
|
flex: 1;
|
|
}
|
|
|
|
|
|
/* Header: wordmark (+ admin tab bar) on the left, quiet log-out link on the
|
|
right.
|
|
|
|
The gap below is 1rem, not the 2rem this had while the screens below it
|
|
opened with an <h1>. Those headings are gone from the list screens (the tab
|
|
bar names them), so 2rem left the content stranded — and on a list the first
|
|
row adds another 0.5rem of its own padding before any text. */
|
|
.sb-standalone .sb-spaces-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
border-bottom: 1px solid #8884;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.sb-standalone .sb-spaces-header-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.sb-standalone .sb-wordmark {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.4em;
|
|
font-size: 1.1rem;
|
|
|
|
// The dock icon already carries its own rounded corners and padding, so it
|
|
// needs no framing here — only a size that sits with the wordmark's cap
|
|
// height rather than towering over it.
|
|
img {
|
|
width: 1.5em;
|
|
height: 1.5em;
|
|
}
|
|
}
|
|
|
|
/* Aligned columns: name | binding link | status | edit action. Each row is a
|
|
subgrid so cells line up across rows while rows keep their borders.
|
|
Non-admin rows simply leave the last column empty. */
|
|
.sb-standalone .sb-space-list {
|
|
list-style: none;
|
|
display: grid;
|
|
grid-template-columns: 1fr max-content max-content max-content;
|
|
}
|
|
|
|
.sb-standalone .sb-space-list li {
|
|
display: grid;
|
|
grid-template-columns: subgrid;
|
|
grid-column: 1 / -1;
|
|
align-items: center;
|
|
column-gap: 1rem;
|
|
padding: 0.5rem 0;
|
|
border-bottom: 1px solid #8884;
|
|
}
|
|
|
|
/* Space name: opens the space itself for every account, admin or not. */
|
|
.sb-standalone .sb-space-link {
|
|
color: var(--ui-accent-color);
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* Admin-only per-row edit control, right-aligned at the end of the row. */
|
|
.sb-standalone .sb-space-edit {
|
|
justify-self: end;
|
|
}
|
|
|
|
/* Bottom-of-form destructive zone. Delete stays visually destructive (via
|
|
.sb-button-danger's colour) but is sized like every other button — it
|
|
shouldn't dominate the page. */
|
|
.sb-standalone .sb-danger-zone {
|
|
margin-top: 2rem;
|
|
padding-top: 1.25rem;
|
|
border-top: 1px solid #8884;
|
|
}
|
|
|
|
.sb-standalone .sb-badge.errored {
|
|
background-color: #b0002033;
|
|
color: #b00020;
|
|
}
|
|
|
|
/* Server-side folder picker (client/spaces_ui/FolderPicker.tsx). */
|
|
.sb-standalone .sb-folder-picker-status {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
margin-top: 0.25rem;
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
.sb-standalone .sb-link-button {
|
|
background: none;
|
|
color: var(--ui-accent-color);
|
|
border: none;
|
|
box-shadow: none;
|
|
padding: 0;
|
|
font-size: inherit;
|
|
font-family: inherit;
|
|
text-decoration: underline;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.sb-standalone .sb-link-button:hover {
|
|
background: none;
|
|
}
|
|
|
|
/* No focus ring on mouse clicks; keep a subtle one for keyboard focus. */
|
|
.sb-standalone .sb-link-button:focus {
|
|
outline: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.sb-standalone .sb-link-button:focus-visible {
|
|
outline: 2px solid var(--ui-accent-color);
|
|
outline-offset: 2px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.sb-standalone .sb-folder-browser {
|
|
margin-top: 0.5rem;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
padding: 0.5rem 0.75rem;
|
|
background: #fafafa;
|
|
}
|
|
|
|
.sb-standalone .sb-folder-crumbs {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.sb-standalone .sb-folder-crumb-sep {
|
|
color: #999;
|
|
}
|
|
|
|
.sb-standalone .sb-folder-dirs {
|
|
list-style: none;
|
|
max-height: 12rem;
|
|
overflow-y: auto;
|
|
margin: 0;
|
|
}
|
|
|
|
.sb-standalone .sb-folder-dirs li {
|
|
padding: 0.15rem 0;
|
|
}
|
|
|
|
.sb-standalone .sb-folder-empty {
|
|
color: var(--subtle-color);
|
|
font-style: italic;
|
|
}
|
|
|
|
/* Users table. */
|
|
.sb-standalone .sb-user-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.sb-standalone .sb-user-table th {
|
|
text-align: left;
|
|
font-weight: 600;
|
|
font-size: 0.85em;
|
|
color: #555;
|
|
padding: 0.35rem 0.5rem;
|
|
}
|
|
|
|
.sb-standalone .sb-user-table td {
|
|
padding: 0.35rem 0.5rem;
|
|
border-bottom: 1px solid #8884;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
// Name reads as the primary control; the Edit button repeats it at the row's
|
|
// end, right-aligned like the spaces list.
|
|
.sb-standalone .sb-user-link {
|
|
color: var(--ui-accent-color);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.sb-standalone .sb-user-table td:last-child {
|
|
text-align: right;
|
|
width: 1%;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.sb-standalone .sb-user-table label {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.3em;
|
|
margin: 0;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.sb-standalone .sb-token-list {
|
|
padding: 0.5rem;
|
|
background: #f7f7f7;
|
|
border-radius: 0.4em;
|
|
list-style: none;
|
|
margin: 0 0 0.5rem;
|
|
}
|
|
|
|
.sb-standalone .sb-token-list li {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.25rem 0;
|
|
}
|
|
|
|
.sb-standalone .sb-token-reveal {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.sb-standalone .sb-token-reveal .sb-input {
|
|
font-family: monospace;
|
|
}
|
|
|
|
.sb-standalone .sb-member-list {
|
|
border: 1px solid #ddd;
|
|
border-radius: 0.4em;
|
|
padding: 0.5rem 0.75rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.sb-standalone .sb-member-list legend {
|
|
font-weight: 600;
|
|
padding: 0 0.3em;
|
|
}
|
|
|
|
.sb-standalone .sb-member-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.4em;
|
|
margin: 0.25rem 0;
|
|
font-weight: normal;
|
|
} |