Livewire Material 2.0.0 aligns every component with Material 3 Expressive and carries no Tailwind anywhere, so SealShare drops tailwindcss and its Vite plugin and writes its views in the package's vocabulary: layout components (<x-pane>, <x-stack>, <x-row>, <x-grid>, <x-form>) with M3's spacing tokens, the md-type-*/md-ink-* text classes, and --md-sys-* tokens in its own small stylesheet. - resources/css/app.css opens with the package's layer order, imports foundation.css and the stylesheet of each component the views render, then the scheme, regenerated with the 2025 colour rules at M3's three contrast levels. The app's own rules follow, one section per view, on tokens and on M3's breakpoints (600/840/1200/1600px) only. - Every view is rewritten in that vocabulary while SealShare keeps the shape it had: the admin table, the admin settings, the recovery codes, the share options and the download page are cards, and their fields fill them rather than stopping at the 40rem bound a card already bounds. The user settings pages became cards too, to match the admin's, each with the sections that stand apart from its one subject — deleting the account, the recovery codes — in a card beside it. Material 3 decides how a component behaves, not whether a container survives: buttons keep their label's width, a form's actions end it, and each heading level keeps one type role. - The layouts clear the floating toolbar by the --material-bottom-toolbar the package publishes, and the snackbar clears it by itself. - The two-factor setup QR code comes from QrCodeService, so it keeps a white field and quiet zone in the dark theme and still scans. - Browse Files is a real button that opens the file input, reachable and visibly focused from the keyboard. - Tests: the design test scans views, JS, CSS and app/ and checks the CSS entry both ways (no missing, no unused import). New Chromium suites cover the frame, settings and admin, the share flow, and every page at M3's breakpoint edges (599/600, 839/840, 1199/1200, 1600px). The package's renamed data-md-* hooks replace the 1.x ones. - Boost's update brings the material-3 guideline and skill and drops the Tailwind skill. composer.json requires nonameweb/livewire-material ^2.0 from the Gitea repository, resolved at the 2.0.0 tag. The CHANGELOG records the move as 2.1.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
408 lines
16 KiB
CSS
408 lines
16 KiB
CSS
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
|
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/foundation.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/layout/grid.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/layout/pane.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/layout/row.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/layout/stack.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/layout/surface.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/account-menu.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/alert.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/badge.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/button.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/card.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/checkbox.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/divider.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/empty-state.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/file.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/form.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/icon.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/input.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/list-item.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/list.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/loading.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/menu-item.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/modal.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/pagination.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/password.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/progress.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/scheme-picker.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/section-nav.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/select.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/shape.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/sort-header.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/stat.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/table.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/textarea.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/theme-toggle.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/toast.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/toggle.css';
|
|
@import '../../vendor/nonameweb/livewire-material/resources/css/components/toolbar.css';
|
|
@import './material-scheme.css';
|
|
|
|
/*
|
|
* SealShare's own rules, unlayered so they outrank every package rule: one section per view, in
|
|
* the order a visitor meets them — the two layouts, the share flow (upload, share created,
|
|
* download), the settings pages in their navigation's order, then admin.
|
|
*/
|
|
|
|
/*
|
|
* resources/views/layouts/app.blade.php: the signed-in and public pages' main column.
|
|
*
|
|
* `<x-pane as="main">` gives the column its horizontal M3 margin (16px below `medium`, 24px from
|
|
* it) and centres it; its cap is SealShare's own 64rem, margins included, which no `width` preset
|
|
* (40, 60, 80rem) matches. The vertical rhythm is the app's own. The bottom padding clears the
|
|
* floating toolbar in partials/toolbar.blade.php by what the toolbar publishes as
|
|
* `--material-bottom-toolbar` (its top edge's distance from the window's bottom, safe area
|
|
* included), plus 16px. Never set `--material-bottom-bar` here: the toolbar reads it to place
|
|
* itself.
|
|
*/
|
|
.app-main {
|
|
max-inline-size: 64rem;
|
|
padding-block-start: var(--md-sys-measurement-space400);
|
|
padding-block-end: calc(var(--material-bottom-toolbar, 0px) + var(--md-sys-measurement-space200));
|
|
}
|
|
|
|
@media (width >= 600px) {
|
|
.app-main {
|
|
padding-block-start: var(--md-sys-measurement-space600);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* resources/views/layouts/auth.blade.php: the centred sign-in card.
|
|
*
|
|
* The card sits in a full-height flex column that top-aligns it below `medium` and centres it
|
|
* from there. The bottom padding clears the floating toolbar exactly as .app-main's does (see
|
|
* above); the horizontal padding and top padding stay flat at every width, as the 1.x layout had.
|
|
*/
|
|
.auth-main {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: flex-start;
|
|
min-block-size: 100dvh;
|
|
padding-inline: var(--md-sys-measurement-space200);
|
|
padding-block-start: var(--md-sys-measurement-space400);
|
|
padding-block-end: calc(var(--material-bottom-toolbar, 0px) + var(--md-sys-measurement-space200));
|
|
}
|
|
|
|
@media (width >= 600px) {
|
|
.auth-main {
|
|
align-items: center;
|
|
}
|
|
}
|
|
|
|
.auth-card {
|
|
inline-size: 100%;
|
|
max-inline-size: 28rem;
|
|
}
|
|
|
|
@media (width >= 600px) {
|
|
.auth-card {
|
|
padding: var(--md-sys-measurement-space400);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/file-uploader.blade.php: the upload page, kept to 1.x's centred 48rem
|
|
* measure (max-w-3xl) inside the 64rem main column, which no `<x-pane>` width preset (40, 60, 80rem)
|
|
* matches. Its text fields stop at 40rem from `medium`; at 64rem the drop zone, the divider and the
|
|
* end-aligned submit ran some 21rem past the fields' edge, at 48rem 8rem.
|
|
*/
|
|
.upload-column {
|
|
max-inline-size: 48rem;
|
|
}
|
|
|
|
/* resources/views/livewire/file-uploader.blade.php: the site's own mark above the title, at 1.x's 5rem-tall size, its width following the image. */
|
|
.upload-site-logo {
|
|
block-size: 5rem;
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/file-uploader.blade.php: the drop zone's dashed outline and its
|
|
* primary tint while dragging. `data-dragging` is Alpine's, not the package's, since no
|
|
* component tracks a native drag over an arbitrary drop target; disabled while an upload runs
|
|
* blocks pointer events and dims to M3's disabled-content opacity, as a code dims elsewhere while
|
|
* busy (.settings-recovery-code--loading).
|
|
*/
|
|
.upload-drop-zone {
|
|
padding: var(--md-sys-measurement-space400);
|
|
border: 2px dashed var(--md-sys-color-outline-variant);
|
|
border-radius: var(--md-sys-shape-corner-xl);
|
|
transition: border-color var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default), background-color var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default);
|
|
}
|
|
|
|
.upload-drop-zone[data-dragging='true'] {
|
|
border-color: var(--md-sys-color-primary);
|
|
background-color: color-mix(in srgb, var(--md-sys-color-primary-container) 40%, transparent);
|
|
}
|
|
|
|
.upload-drop-zone[aria-disabled='true'] {
|
|
pointer-events: none;
|
|
opacity: var(--md-sys-state-disabled-content-opacity);
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/file-uploader.blade.php: the drop zone's shape morphs into a burst
|
|
* while files are dragged over it — SealShare's signature, kept from 1.x (docs/reference/m3/styles.md
|
|
* § Shape: "Shape morph should respond to user interaction"). Two `<x-shape>`s sit
|
|
* stacked (`inset: 0` on an absolutely positioned element sizes it to the box, no width/height
|
|
* class needed) and cross-fade/scale on the spatial-slow spring the shape's size warrants
|
|
* (docs/reference/m3/styles.md § Motion: "larger elements may use slow"); opacity rides the
|
|
* effects-slow spring beside it, since a colour or fade must never overshoot. Reduced motion needs
|
|
* no local override: the tokens themselves zero out under it (tokens/motion.css).
|
|
*/
|
|
.upload-drop-shapes {
|
|
position: relative;
|
|
display: grid;
|
|
place-items: center;
|
|
inline-size: 7rem;
|
|
block-size: 7rem;
|
|
}
|
|
|
|
.upload-drop-shape {
|
|
position: absolute;
|
|
inset: 0;
|
|
transition: scale var(--md-sys-motion-spatial-slow-duration) var(--md-sys-motion-spatial-slow), rotate var(--md-sys-motion-spatial-slow-duration) var(--md-sys-motion-spatial-slow), opacity var(--md-sys-motion-effects-slow-duration) var(--md-sys-motion-effects-slow);
|
|
}
|
|
|
|
.upload-drop-shape--idle {
|
|
scale: 1;
|
|
rotate: 0deg;
|
|
opacity: 1;
|
|
color: var(--md-sys-color-secondary-container);
|
|
}
|
|
|
|
.upload-drop-zone[data-dragging='true'] .upload-drop-shape--idle {
|
|
scale: 0.5;
|
|
rotate: 45deg;
|
|
opacity: 0;
|
|
}
|
|
|
|
.upload-drop-shape--burst {
|
|
scale: 0.5;
|
|
rotate: -45deg;
|
|
opacity: 0;
|
|
color: var(--md-sys-color-primary-container);
|
|
}
|
|
|
|
.upload-drop-zone[data-dragging='true'] .upload-drop-shape--burst {
|
|
scale: 1.1;
|
|
rotate: 0deg;
|
|
opacity: 1;
|
|
}
|
|
|
|
.upload-drop-icon {
|
|
position: relative;
|
|
color: var(--md-sys-color-on-secondary-container);
|
|
transition: color var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default);
|
|
}
|
|
|
|
.upload-drop-zone[data-dragging='true'] .upload-drop-icon {
|
|
color: var(--md-sys-color-on-primary-container);
|
|
}
|
|
|
|
/* resources/views/livewire/file-uploader.blade.php: the "Processing files..." indicator at 1.x's smaller size, beside its label. */
|
|
.upload-processing-indicator {
|
|
inline-size: 2rem;
|
|
block-size: 2rem;
|
|
}
|
|
|
|
/* resources/views/livewire/file-uploader.blade.php: the selected-files list scrolls on its own past 1.x's cap instead of pushing the options and the submit button down the page. */
|
|
.upload-file-list {
|
|
max-block-size: 18rem;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/share-created.blade.php: the page a share's link is ready on, kept to
|
|
* 1.x's centred 32rem measure, which no `<x-pane>` width preset (40, 60, 80rem) matches.
|
|
*/
|
|
.share-column {
|
|
max-inline-size: 32rem;
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/share-created.blade.php: the check that settles onto its Expressive
|
|
* shape once the link is ready (the `share-ready`/`share-ready-fade` keyframes after it). The shape
|
|
* sits at the box's edges (`inset: 0` on an absolutely positioned element sizes it, no width/height
|
|
* class needed); both colours are container roles `md-ink-*` has no class for, so they are the
|
|
* application's own CSS rather than a component prop.
|
|
*/
|
|
.share-check {
|
|
position: relative;
|
|
display: grid;
|
|
place-items: center;
|
|
inline-size: 6rem;
|
|
block-size: 6rem;
|
|
animation:
|
|
share-ready var(--md-sys-motion-spatial-slow-duration) var(--md-sys-motion-spatial-slow) both,
|
|
share-ready-fade var(--md-sys-motion-effects-slow-duration) var(--md-sys-motion-effects-slow) both;
|
|
}
|
|
|
|
.share-check-shape {
|
|
position: absolute;
|
|
inset: 0;
|
|
color: var(--md-sys-color-primary-container);
|
|
}
|
|
|
|
.share-check-icon {
|
|
/* Without this the icon, though later in the DOM, is a non-positioned in-flow child: it paints
|
|
before the absolutely positioned shape beside it (CSS's stacking order for z-index:auto) and
|
|
sits hidden underneath it, as .upload-drop-icon's own position: relative is there to avoid. */
|
|
position: relative;
|
|
color: var(--md-sys-color-on-primary-container);
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/share-created.blade.php: the check settling onto its shape, run by
|
|
* .share-check — rotate and scale on the spatial spring (shape motion), opacity on effects beside
|
|
* it, since M3 never lets a colour or fade overshoot; reduced motion needs no local override, the
|
|
* duration tokens themselves zero out under it.
|
|
*/
|
|
@keyframes share-ready {
|
|
from {
|
|
rotate: -90deg;
|
|
scale: 0.4;
|
|
}
|
|
|
|
to {
|
|
rotate: 0deg;
|
|
scale: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes share-ready-fade {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/share-created.blade.php: the QR code dialog. `App\Services\QrCodeService`
|
|
* already draws its SVG black on white with a four-module quiet zone, so the container adds no
|
|
* colour of its own — no colour class or literal colour could give it one that also holds in dark
|
|
* mode. The corner only rounds the container that clips it, exactly as .settings-two-factor-qr's does.
|
|
*/
|
|
.share-qr {
|
|
display: grid;
|
|
place-items: center;
|
|
inline-size: 100%;
|
|
max-inline-size: 20rem;
|
|
aspect-ratio: 1;
|
|
margin-inline: auto;
|
|
overflow: hidden;
|
|
border-radius: var(--md-sys-shape-corner-lg);
|
|
}
|
|
|
|
.share-qr svg {
|
|
inline-size: 100%;
|
|
block-size: 100%;
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/share-download.blade.php: the page a recipient opens, kept to the same
|
|
* centred 32rem measure as share-created.blade.php.
|
|
*/
|
|
.download-column {
|
|
max-inline-size: 32rem;
|
|
}
|
|
|
|
/* resources/views/livewire/share-download.blade.php: the site's own mark above the title, at 1.x's 5rem-tall size. */
|
|
.download-site-logo {
|
|
block-size: 5rem;
|
|
}
|
|
|
|
/* resources/views/partials/settings-heading.blade.php: the settings pages' shared heading, spaced above their section navigation and content. */
|
|
.settings-heading {
|
|
margin-block-end: var(--md-sys-measurement-space300);
|
|
}
|
|
|
|
/*
|
|
* resources/views/pages/settings/layout.blade.php: the settings cards inside the 64rem main
|
|
* column. 1.x kept the form itself to 32rem (max-w-lg) under a full-width heading; the card now
|
|
* holds both, so the cap moves to the card and gains the card's own 16dp of side padding — 34rem,
|
|
* which leaves the fields at 1.x's measure. The section navigation above stays full width, as it
|
|
* was.
|
|
*/
|
|
.settings-column {
|
|
max-inline-size: 34rem;
|
|
}
|
|
|
|
/*
|
|
* resources/views/pages/settings/two-factor.blade.php: the setup QR code. Fortify's own
|
|
* twoFactorQrCodeSvg() draws no quiet zone, so the SVG comes from App\Services\QrCodeService
|
|
* against the same otpauth URL instead, which bakes in its own white field and four-module quiet
|
|
* zone — the only way to guarantee one in dark mode, since no colour class or literal colour can
|
|
* paint it onto 2.0.0's foundation. Sized at 1.x's 16rem square, corners rounded and clipped to
|
|
* match the settings surfaces around it.
|
|
*/
|
|
.settings-two-factor-qr {
|
|
display: grid;
|
|
place-items: center;
|
|
inline-size: 16rem;
|
|
aspect-ratio: 1;
|
|
overflow: hidden;
|
|
border-radius: var(--md-sys-shape-corner-lg);
|
|
}
|
|
|
|
.settings-two-factor-qr svg {
|
|
inline-size: 100%;
|
|
block-size: 100%;
|
|
}
|
|
|
|
/*
|
|
* resources/views/pages/settings/two-factor/recovery-codes.blade.php: a code dims to M3's disabled
|
|
* content opacity while regenerateRecoveryCodes() is in flight, and back, on the effects spring
|
|
* instead of Tailwind's animate-pulse loop — 2.0.0 keeps no keyframe utility for it. The
|
|
* transition sits on the code itself so the way back eases too; under reduced motion the token's
|
|
* duration is 0ms.
|
|
*/
|
|
.settings-recovery-code {
|
|
transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast);
|
|
}
|
|
|
|
.settings-recovery-code--loading {
|
|
opacity: var(--md-sys-state-disabled-content-opacity);
|
|
}
|
|
|
|
/*
|
|
* resources/views/pages/settings/appearance.blade.php: the theme picker stays a comfortable
|
|
* width instead of stretching the full settings column. No `<x-group>` width prop caps it, and
|
|
* 24rem matches no `<x-pane>` preset.
|
|
*/
|
|
.settings-appearance-picker {
|
|
max-inline-size: 24rem;
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/admin/admin-dashboard.blade.php: the shares table scrolls sideways on
|
|
* its own, on a window too narrow for every column, instead of the page around it.
|
|
*/
|
|
.admin-shares-table-scroll {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/admin/admin-settings.blade.php: the settings keep 1.x's own narrower
|
|
* measure inside the 64rem main column, 42rem, which no `<x-pane>` width preset (40, 60, 80rem)
|
|
* matches; the pane centres it.
|
|
*/
|
|
.admin-settings {
|
|
max-inline-size: 42rem;
|
|
}
|
|
|
|
/*
|
|
* resources/views/livewire/admin/admin-settings.blade.php: the current and previewed site logo,
|
|
* at 1.x's 4rem height with its width following the image's own ratio. M3 keeps no size scale for
|
|
* a plain <img>.
|
|
*/
|
|
.admin-settings-logo {
|
|
block-size: 4rem;
|
|
border-radius: var(--md-sys-shape-corner-sm);
|
|
}
|