Files
dozzle/assets/main.css
T

393 lines
10 KiB
CSS

@import "tailwindcss";
@import "splitpanes/dist/splitpanes.css" layer(base);
@import "@fontsource/jetbrains-mono/400.css";
@import "@fontsource/jetbrains-mono/500.css";
@import "@fontsource/jetbrains-mono/600.css";
@import "@fontsource/jetbrains-mono/700.css";
@plugin "daisyui";
@plugin "@tailwindcss/typography";
@theme {
--color-green: oklch(0.62 0.119722 158.82);
--color-red: oklch(64% 0.218 28.85);
--color-purple: oklch(51.49% 0.215 321.03);
--color-blue: oklch(65% 0.171 249.5);
--color-orange: oklch(85% 0.186 48.13);
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
}
@utility pt-safe {
padding-top: env(safe-area-inset-top);
}
@custom-variant max-h-500 {
@media (max-height: 500px) {
@slot;
}
}
@plugin "daisyui/theme" {
name: "dark";
default: false;
prefersdark: false;
color-scheme: dark;
--color-base-100: oklch(25% 0 0);
--color-base-200: oklch(18% 0 0);
--color-base-300: oklch(11% 0 0);
--color-base-content: oklch(89.23% 0 0);
--color-primary: oklch(70.96% 0.143 176.65);
--color-primary-content: oklch(98% 0.01 240);
--color-secondary: oklch(81.38% 0.1448 90.1243);
--color-secondary-content: oklch(98% 0.01 200);
--color-neutral: oklch(50% 0.05 240);
--color-neutral-content: oklch(98% 0.01 240);
--color-info: var(--color-blue);
--color-info-content: oklch(98% 0.01 220);
--color-success: var(--color-green);
--color-success-content: oklch(98% 0.01 140);
--color-warning: var(--color-orange);
--color-success-content: oklch(98% 0.01 140);
--color-error: var(--color-red);
--color-error-content: oklch(98% 0.01 30);
/* ANSI log palette tuned for legibility on the dark background */
--ansi-black: oklch(45% 0 0);
--ansi-red: oklch(65% 0.2 25);
--ansi-green: oklch(72% 0.16 150);
--ansi-yellow: oklch(80% 0.13 85);
--ansi-blue: oklch(70% 0.14 250);
--ansi-magenta: oklch(68% 0.19 330);
--ansi-cyan: oklch(75% 0.11 200);
--ansi-white: oklch(89% 0 0);
--ansi-bright-black: oklch(60% 0 0);
--ansi-bright-red: oklch(72% 0.2 25);
--ansi-bright-green: oklch(82% 0.17 150);
--ansi-bright-yellow: oklch(88% 0.14 95);
--ansi-bright-blue: oklch(78% 0.13 250);
--ansi-bright-magenta: oklch(75% 0.19 330);
--ansi-bright-cyan: oklch(85% 0.11 200);
--ansi-bright-white: oklch(100% 0 0);
--radius-selector: 1rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--border: 1px;
--depth: 0;
--noise: 1;
}
@plugin "daisyui/theme" {
name: "light";
default: false;
prefersdark: false;
color-scheme: light;
--color-base-100: oklch(100% 0 0);
--color-base-200: oklch(97% 0 0);
--color-base-300: oklch(90% 0 0);
--color-base-content: oklch(33% 0 0);
--color-primary: oklch(79.96% 0.143 176.65);
--color-primary-content: oklch(98% 0.01 240);
--color-secondary: oklch(82.67% 0.15380739746702807 77.82);
--color-secondary-content: oklch(98% 0.01 200);
--color-neutral: oklch(50% 0.05 240);
--color-neutral-content: oklch(98% 0.01 240);
--color-info: var(--color-blue);
--color-info-content: oklch(98% 0.01 220);
--color-success: var(--color-green);
--color-success-content: oklch(98% 0.01 140);
--color-warning: var(--color-orange);
--color-success-content: oklch(98% 0.01 140);
--color-error: var(--color-red);
--color-error-content: oklch(98% 0.01 30);
/* ANSI log palette tuned for legibility on the light background */
--ansi-black: oklch(25% 0 0);
--ansi-red: oklch(52% 0.2 25);
--ansi-green: oklch(52% 0.15 150);
--ansi-yellow: oklch(60% 0.13 75);
--ansi-blue: oklch(50% 0.17 255);
--ansi-magenta: oklch(50% 0.2 330);
--ansi-cyan: oklch(55% 0.1 210);
--ansi-white: oklch(70% 0 0);
--ansi-bright-black: oklch(45% 0 0);
--ansi-bright-red: oklch(58% 0.21 25);
--ansi-bright-green: oklch(58% 0.16 150);
--ansi-bright-yellow: oklch(62% 0.13 85);
--ansi-bright-blue: oklch(55% 0.16 255);
--ansi-bright-magenta: oklch(56% 0.2 330);
--ansi-bright-cyan: oklch(60% 0.1 210);
--ansi-bright-white: oklch(85% 0 0);
--radius-selector: 1rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--border: 1px;
--depth: 0;
--noise: 1;
}
@utility input {
@apply outline-hidden!;
&.input-ghost {
@apply focus:border-transparent;
}
}
@utility btn {
&:not(.btn-primary):not(.btn-secondary) {
@apply hover:bg-base-100;
}
}
/* daisyUI darkens the ghost button's border on hover, which reads as a
* near-black ring on the dark theme. Use a theme-aware base-content tint so the
* hover ring stays visible and legible in both themes (light on dark, dark on
* light) instead of a fixed near-black. */
.btn-ghost:hover {
border-color: color-mix(in oklab, var(--color-base-content) 40%, transparent) !important;
}
@utility menu-active {
&,
&:active {
--menu-active-bg: var(--color-primary);
}
}
@utility animate-bounce-fast {
animation: bounce 0.5s 2 both;
}
@utility link-primary {
@apply underline-offset-4 hover:underline;
}
@layer base {
@media screen and (max-device-width: 480px) {
body {
-webkit-text-size-adjust: 100%;
}
}
}
/* Reserve the document scrollbar gutter so the layout width stays constant when
* a page-level scrollbar appears (e.g. once a container's logs load). Without
* this, the width jump makes the percentage-based splitpanes recompute and the
* sidebar visibly flicks. */
html {
scrollbar-gutter: stable;
}
body {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
/* Height of the mobile top nav (logo row + border). The container status bar
* sticks right below it and the log content is padded by the same amount, so
* they stay locked together. The safe-area inset is already handled by body. */
:root {
--mobile-nav-height: 57px;
/* Viewport offset of the nav's bottom edge. The nav is fixed to the viewport
* so it sits above the body's safe-area padding, which means anything sticking
* to it must add the inset back in. */
--mobile-nav-offset: calc(var(--mobile-nav-height) + env(safe-area-inset-top));
}
[class*="shadow-"] {
@apply shadow-base-content/8;
}
.splitpanes--vertical .splitpanes__pane {
transition: none !important;
}
/* CodeMirror autocomplete tooltip styles */
.cm-tooltip {
@apply bg-base-200! border-base-content/40! min-w-96 rounded-sm border shadow-md;
}
.cm-tooltip-autocomplete ul {
@apply font-sans;
}
.cm-tooltip-autocomplete ul li {
@apply my-1 px-2;
}
.cm-tooltip-autocomplete ul li[aria-selected] {
@apply bg-primary/20 text-base-content!;
}
.cm-completionLabel {
@apply text-base-content!;
}
.cm-completionDetail {
@apply text-base-content/60! ml-2 italic;
}
.cm-completionMatchedText {
@apply text-primary! font-bold no-underline;
}
.cm-completionIcon {
@apply mr-2 opacity-70;
}
.cm-editor.cm-focused {
outline: none;
}
.cm-scroller {
font-family: var(--font-mono);
}
.status-pill {
@apply inline-flex items-center gap-1.5 rounded border px-2 py-0.5 font-mono text-xs font-medium tracking-wider uppercase;
}
.status-pill-neutral {
@apply bg-base-100 border-base-content/15 text-base-content/70;
}
.status-pill-success {
@apply text-success border-success/30 bg-success/10;
}
.status-pill-primary {
@apply text-primary border-primary/30 bg-primary/10;
}
.status-pill-secondary {
@apply text-secondary border-secondary/30 bg-secondary/10;
}
.status-pill-warning {
@apply text-warning border-warning/30 bg-warning/10;
}
.status-pill-error {
@apply text-error border-error/30 bg-error/10;
}
/* --- Icon button motion ---------------------------------------------------
* Icon-only buttons get a small spring on hover and a press-in on click. The
* transform sits on the glyph rather than the button so the circle and border
* are left alone. This includes daisyUI `swap` icons: swap-rotate animates the
* standalone `rotate` property, which composes with `transform` rather than
* fighting it (swap-flip, which does use `transform`, is unused here).
*
* `btn-circle` and `btn-square` are icon-only by definition; `.icon-btn` opts
* in a bare <button>/<a> or a daisyUI button that is icon-only without being
* one of those shapes.
*
* Everything composes through custom properties instead of competing
* `transform` declarations, so a per-icon flavour below only has to set a
* variable and never has to out-specify the generic hover rule. */
:is(.btn-circle, .btn-square, .icon-btn) :where(svg, img) {
--icon-scale: 1;
--icon-rotate: 0deg;
--icon-lift: 0px;
transform: translateY(var(--icon-lift)) rotate(var(--icon-rotate)) scale(var(--icon-scale));
/* Includes the standalone rotate/scale/translate properties so a Tailwind
* `rotate-180` toggle on the glyph keeps animating rather than snapping, and
* opacity so this rule winning over daisyUI's `.swap > *` does not kill the
* swap crossfade. */
transition-property: transform, rotate, scale, translate, opacity;
transition-duration: 200ms;
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
}
.btn-circle,
.btn-square,
.icon-btn {
@apply cursor-pointer;
}
:is(.btn-circle, .btn-square, .icon-btn):hover :where(svg, img) {
--icon-scale: 1.15;
}
:is(.btn-circle, .btn-square, .icon-btn):active :where(svg, img) {
--icon-scale: 0.92;
}
/* Per-icon flavours, opted into by the icon itself. */
.btn:hover .icon-spin {
--icon-rotate: 90deg;
}
.btn:hover .icon-float {
--icon-lift: -2px;
}
/* The two animated flavours restate the hover scale in their keyframes so they
* settle at the same size the generic hover rule would have given them. */
.btn:hover .icon-wiggle {
animation: icon-wiggle 600ms ease-in-out;
}
.btn:hover .icon-ring {
animation: icon-ring 700ms ease-in-out;
transform-origin: top center;
}
@keyframes icon-wiggle {
0%,
100% {
transform: rotate(0deg) scale(1.15);
}
25% {
transform: rotate(-14deg) scale(1.15);
}
60% {
transform: rotate(10deg) scale(1.15);
}
80% {
transform: rotate(-5deg) scale(1.15);
}
}
@keyframes icon-ring {
0%,
100% {
transform: rotate(0deg) scale(1.15);
}
15% {
transform: rotate(14deg) scale(1.15);
}
35% {
transform: rotate(-12deg) scale(1.15);
}
55% {
transform: rotate(8deg) scale(1.15);
}
75% {
transform: rotate(-4deg) scale(1.15);
}
}
@media (prefers-reduced-motion: reduce) {
:is(.btn-circle, .btn-square, .icon-btn) :where(svg, img),
.btn:hover .icon-wiggle,
.btn:hover .icon-ring {
animation: none !important;
transform: none !important;
transition: none !important;
}
}