diff --git a/assets/fonts/comfortaa-v40-latin-regular.woff b/assets/fonts/comfortaa-v40-latin-regular.woff deleted file mode 100644 index c54393b..0000000 Binary files a/assets/fonts/comfortaa-v40-latin-regular.woff and /dev/null differ diff --git a/assets/fonts/comfortaa-v40-latin-regular.woff2 b/assets/fonts/comfortaa-v40-latin-regular.woff2 deleted file mode 100644 index bc4da8b..0000000 Binary files a/assets/fonts/comfortaa-v40-latin-regular.woff2 and /dev/null differ diff --git a/assets/fonts/open-sans-v34-latin-regular.woff b/assets/fonts/open-sans-v34-latin-regular.woff deleted file mode 100644 index b083626..0000000 Binary files a/assets/fonts/open-sans-v34-latin-regular.woff and /dev/null differ diff --git a/src/index.scss b/src/index.scss index 8c4500a..d6d8b85 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,275 +1,8 @@ -@use 'style/mixins' as *; @use 'style/common'; - -html > body { - width: 100%; - height: 100%; - display: flex; - position: relative; - - > .canvas-container { - height: 100%; - width: 100%; - display: flex; - - > canvas { - height: 100%; - width: 100%; - touch-action: none; - cursor: - url('../assets/icons/brush.svg') 0 24, - auto; - } - - > .errors-container { - position: absolute; - top: 0; - left: 0; - margin: var(--normal-margin); - - pre { - font-size: 20px; - color: red; - } - } - - .counters { - @include blurred-background(white); - position: absolute; - border-radius: var(--border-radius); - padding: var(--small-margin); - - @include on-large-screen { - top: var(--normal-margin); - right: var(--normal-margin); - } - - @include on-small-screen { - bottom: var(--normal-margin); - right: var(--normal-margin); - } - } - } - - > aside { - @include blurred-background(#fff); - box-shadow: var(--shadow); - display: flex; - position: absolute; - overflow: hidden; - - @include on-large-screen { - top: 50%; - left: 0; - transform: translateY(-50%); - max-height: 350px; - } - - @include on-small-screen { - top: 0; - left: 50%; - transform: translateX(-50%); - flex-direction: column; - } - - transition: opacity var(--transition-time-long); - border-radius: var(--border-radius); - margin: var(--small-margin); - - > nav.buttons { - @include center-children; - justify-content: space-evenly; - - @include on-large-screen { - flex-direction: column; - } - - > button { - position: relative; - border: none; - background-color: transparent; - cursor: pointer; - - @include square(var(--icon-size)); - margin: var(--small-margin); - - &::before, - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - } - - &::before { - background-color: var(--accent-color); - - @include on-large-screen { - width: 0; - border-radius: 0 var(--border-radius) var(--border-radius) 0; - transition: - background-color var(--transition-time), - width var(--transition-time); - left: calc(-1 * var(--small-margin)); - height: 140%; - top: 50%; - transform: translateY(-50%); - } - - @include on-small-screen { - height: 0; - border-radius: 0 0 var(--border-radius) var(--border-radius); - transition: - background-color var(--transition-time), - height var(--transition-time); - top: calc(-1 * var(--small-margin)); - width: 140%; - left: 50%; - transform: translateX(-50%); - } - } - - &::after { - background-color: var(--accent-color); - - transition: - transform var(--transition-time), - background-color var(--transition-time); - - mask-repeat: no-repeat; - - @include square(var(--icon-size)); - } - - &.active { - &::before { - @include on-large-screen { - width: calc(100% + 2 * var(--small-margin)); - } - - @include on-small-screen { - height: calc(100% + 2 * var(--small-margin)); - } - } - - &::after { - background-color: white; - } - } - - &:hover::after { - transform: scale(1.15); - } - - &.info::after { - mask-image: url('../assets/icons/info.svg'); - } - &.maximize-full-screen::after { - mask-image: url('../assets/icons/maximize.svg'); - } - &.minimize-full-screen::after { - mask-image: url('../assets/icons/minimize.svg'); - } - &.settings::after { - mask-image: url('../assets/icons/settings.svg'); - } - &.restart::after { - mask-image: url('../assets/icons/restart.svg'); - } - } - } - - > main.pages { - overflow-x: hidden; - overflow-y: auto; - scrollbar-width: thin; - scrollbar-color: var(--main-color) transparent; - &::-webkit-scrollbar-track, - &::-webkit-scrollbar { - background-color: transparent; - width: 6px; - } - &::-webkit-scrollbar-thumb { - background-color: var(--main-color); - border-radius: var(--border-radius); - } - - &, - > * { - transition: - width var(--transition-time-long), - height var(--transition-time-long); - - @include on-large-screen { - width: max(500px, 10vw); - } - - @include on-small-screen { - height: max(500px, 70vh); - } - } - - &.hidden { - @include on-large-screen { - width: 0; - } - - @include on-small-screen { - height: 0; - } - } - - > section { - padding: var(--normal-margin); - display: flex; - flex-direction: column; - - .slider { - $track-height: 12px; - margin-bottom: var(--small-margin); - user-select: none; - - p { - display: flex; - justify-content: space-between; - } - - input[type='range'] { - width: 100%; - height: $track-height; - appearance: none; - background: transparent; - outline: none; - border-radius: 1000px; - - &::-webkit-slider-runnable-track { - appearance: none; - cursor: pointer; - border-radius: 1000px; - @include square(15px); - background: var(--accent-color); - } - - &::-webkit-slider-thumb { - appearance: none; - cursor: pointer; - border-radius: 1000px; - $size: 24px; - @include square($size); - background: white; - box-shadow: 0 0 5px 1px var(--accent-color); - - transform: translateY(-5px); - transition: transform var(--transition-time); - &:hover { - transform: translateY(-5px) scale(1.1); - } - } - } - } - } - } - } -} +@use 'style/app-shell'; +@use 'style/garden-prompt'; +@use 'style/control-dock'; +@use 'style/toolbar'; +@use 'style/config-pane'; +@use 'style/panels'; +@use 'style/loading'; diff --git a/src/style/_app-shell.scss b/src/style/_app-shell.scss new file mode 100644 index 0000000..abf8824 --- /dev/null +++ b/src/style/_app-shell.scss @@ -0,0 +1,133 @@ +html > body.is-loading .perf-stats-overlay { + display: none; +} + +$grain-noise-a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='257' height='257' viewBox='0 0 257 257'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' seed='17' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='257' height='257' filter='url(%23n)'/%3E%3C/svg%3E"); +$grain-noise-b: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='389' height='389' viewBox='0 0 389 389'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.53' numOctaves='5' seed='41' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='389' height='389' filter='url(%23n)'/%3E%3C/svg%3E"); + +html > body { + width: 100%; + height: 100vh; + height: 100dvh; + overflow: hidden; + display: flex; + position: relative; + background: var(--garden-background, #10151f); + + > .canvas-container { + height: 100%; + width: 100%; + display: flex; + position: relative; + overflow: hidden; + + > canvas { + position: relative; + z-index: 0; + height: 100%; + width: 100%; + touch-action: none; + } + + > .garden-grain { + --garden-grain-strength: 0; + + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; + contain: strict; + + &::before, + &::after { + content: ''; + position: absolute; + inset: 0; + } + + &::before { + opacity: clamp(0, calc(var(--garden-grain-strength) * 4.25), 0.24); + background-image: $grain-noise-a; + background-size: 257px 257px; + filter: contrast(145%) brightness(0.82); + mix-blend-mode: multiply; + } + + &::after { + opacity: clamp(0, calc(var(--garden-grain-strength) * 2.5), 0.12); + background-image: $grain-noise-b; + background-position: 73px 41px; + background-size: 389px 389px; + filter: contrast(135%) brightness(1); + mix-blend-mode: screen; + transform: rotate(0.01deg); + } + + &[hidden] { + display: none; + } + } + + > .eraser-preview { + position: absolute; + top: 0; + left: 0; + z-index: 3; + width: var(--eraser-preview-size, 96px); + height: var(--eraser-preview-size, 96px); + border: 2px solid rgb(255 234 228 / 88%); + border-radius: 50%; + background: rgb(255 140 117 / 13%); + box-shadow: + 0 0 0 1px rgb(255 88 70 / 34%), + 0 0 26px rgb(255 118 92 / 24%); + opacity: 0; + pointer-events: none; + transform: translate(-50%, -50%); + transition: + opacity var(--transition-time), + width var(--transition-time), + height var(--transition-time); + mix-blend-mode: screen; + + &.visible { + opacity: 1; + } + } + + > .perf-stats-overlay { + position: absolute; + top: max(8px, env(safe-area-inset-top)); + left: max(8px, env(safe-area-inset-left)); + z-index: 6; + padding: 6px 8px; + border: 1px solid rgb(255 255 255 / 18%); + border-radius: 6px; + background: rgb(0 0 0 / 62%); + color: rgb(255 255 255 / 92%); + font: + 600 12px/1.35 ui-monospace, + SFMono-Regular, + Menlo, + Consolas, + monospace; + white-space: pre; + pointer-events: none; + user-select: none; + box-shadow: 0 8px 24px rgb(0 0 0 / 28%); + } + + > .errors-container { + position: absolute; + top: 0; + left: 0; + margin: var(--normal-margin); + z-index: 5; + + pre { + font-size: 20px; + color: red; + } + } + } +} diff --git a/src/style/_config-pane.scss b/src/style/_config-pane.scss new file mode 100644 index 0000000..ad7ae31 --- /dev/null +++ b/src/style/_config-pane.scss @@ -0,0 +1,309 @@ +@use 'mixins' as *; + +.config-pane-container { + --config-pane-available-height: calc( + 100vh - 24px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) + ); + + position: fixed; + top: max(12px, env(safe-area-inset-top, 0px)); + right: max(12px, env(safe-area-inset-right, 0px)); + display: grid; + grid-template-rows: auto minmax(0, 1fr); + gap: 4px; + z-index: 20; + width: min( + 420px, + calc(100vw - 24px - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)) + ); + max-height: var(--config-pane-available-height); + pointer-events: none; + + @supports (height: 100dvh) { + --config-pane-available-height: calc( + 100dvh - 24px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) + ); + } +} + +.config-pane-container--open { + pointer-events: auto; +} + +.config-pane { + --tp-blade-value-width: min(260px, 64%); + + width: 100%; + max-height: calc(var(--config-pane-available-height) - 36px); + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + pointer-events: auto; + scrollbar-width: thin; + touch-action: pan-y; + -webkit-overflow-scrolling: touch; + + // Tweakpane v4 internal classes — re-verify on upgrade. + // No public theming hook exists for label padding or the slider/number + // flex ratio; if a fourth override appears here, switch to a custom plugin. + .tp-lblv_l { + padding-right: 10px; + } + + .tp-sldtxtv_s { + flex: 1 1 auto; + min-width: 0; + } + + .tp-sldtxtv_t { + flex: 0 0 54px; + } +} + +.config-pane-close { + position: relative; + justify-self: end; + display: grid; + width: 28px; + height: 28px; + place-items: center; + border: 0; + border-radius: 4px; + background: transparent; + color: rgb(235 238 245 / 82%); + cursor: pointer; + font: inherit; + font-size: 0; + pointer-events: auto; + transition: + background-color var(--transition-time), + color var(--transition-time); + + &::before, + &::after { + content: ''; + position: absolute; + width: 14px; + height: 2px; + border-radius: 999px; + background: currentColor; + } + + &::before { + transform: rotate(45deg); + } + + &::after { + transform: rotate(-45deg); + } + + &:hover { + background: rgb(255 255 255 / 10%); + color: white; + } + + &:focus-visible { + outline: 2px solid white; + outline-offset: -2px; + } + + &[hidden] { + display: none; + } +} + +@mixin mobile-config-pane() { + .config-pane-container { + --config-pane-available-height: min( + 64vh, + calc( + 100vh - 112px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) + ) + ); + + top: max(8px, env(safe-area-inset-top, 0px)); + right: auto; + left: 50%; + width: min(80vw, 420px); + transform: translateX(-50%); + + @supports (height: 100dvh) { + --config-pane-available-height: min( + 64dvh, + calc( + 100dvh - + 112px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) + ) + ); + } + } + + .config-pane { + --tp-blade-value-width: min(210px, 62%); + --tp-container-unit-size: 18px; + + padding-bottom: 10px; + font-size: 11px; + + // Tweakpane v4 internal class — re-verify on upgrade. + .tp-sldtxtv_t { + flex-basis: 48px; + } + } + + .config-pane-close { + width: 32px; + height: 32px; + } +} + +@include on-mobile-input { + @include mobile-config-pane; +} + +.color-reaction-matrix-blade { + padding: 6px 8px 8px; +} + +.color-reaction-matrix { + display: grid; + grid-template-columns: 28px repeat(3, minmax(0, 1fr)); + gap: 4px; + align-items: stretch; +} + +.color-reaction-matrix__corner, +.color-reaction-matrix__header { + display: flex; + min-width: 0; + min-height: 28px; + align-items: center; + justify-content: center; + color: rgb(255 255 255 / 76%); + font-size: 11px; + line-height: 1; +} + +.color-reaction-matrix__header { + gap: 0; +} + +.color-reaction-matrix__corner { + justify-content: flex-start; + padding-left: 2px; + color: rgb(255 255 255 / 62%); +} + +.color-reaction-matrix__swatch { + flex: 0 0 auto; + width: 12px; + height: 12px; + border: 1px solid rgb(255 255 255 / 55%); + border-radius: 999px; + box-shadow: 0 0 0 1px rgb(0 0 0 / 18%); +} + +.color-reaction-matrix__cell { + min-width: 0; + display: grid; +} + +.color-reaction-matrix__button { + position: relative; + display: grid; + width: 100%; + min-width: 0; + height: 28px; + place-items: center; + border: 1px solid rgb(255 255 255 / 16%); + border-radius: 4px; + padding: 0 4px; + background: rgb(255 255 255 / 8%); + color: white; + font: inherit; + cursor: pointer; + transition: + background-color var(--transition-time), + border-color var(--transition-time), + color var(--transition-time), + transform var(--transition-time); +} + +.color-reaction-matrix__button:hover { + transform: translateY(-1px); +} + +.color-reaction-matrix__button:focus-visible { + outline: 2px solid rgb(255 255 255 / 72%); + outline-offset: 1px; +} + +.color-reaction-matrix__button[data-reaction='follow'] { + border-color: rgb(115 235 160 / 44%); + background: rgb(53 165 96 / 20%); + color: rgb(157 255 195 / 94%); +} + +.color-reaction-matrix__button[data-reaction='ignore'] { + border-color: rgb(255 255 255 / 18%); + background: rgb(255 255 255 / 7%); + color: rgb(235 238 245 / 72%); +} + +.color-reaction-matrix__button[data-reaction='avoid'] { + border-color: rgb(255 145 120 / 46%); + background: rgb(215 74 54 / 19%); + color: rgb(255 171 148 / 94%); +} + +.color-reaction-matrix__icon { + position: relative; + display: block; + width: 16px; + height: 16px; +} + +.color-reaction-matrix__icon::before, +.color-reaction-matrix__icon::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + border-radius: 999px; + background: currentColor; + transform: translate(-50%, -50%); +} + +.color-reaction-matrix__button[data-reaction='follow'] + > .color-reaction-matrix__icon::before, +.color-reaction-matrix__button[data-reaction='avoid'] + > .color-reaction-matrix__icon::before { + width: 14px; + height: 2px; +} + +.color-reaction-matrix__button[data-reaction='follow'] + > .color-reaction-matrix__icon::after { + width: 2px; + height: 14px; +} + +.color-reaction-matrix__button[data-reaction='avoid'] + > .color-reaction-matrix__icon::after { + display: none; +} + +.color-reaction-matrix__button[data-reaction='ignore'] > .color-reaction-matrix__icon { + width: 12px; + height: 12px; + border: 2px solid currentColor; + border-radius: 999px; + opacity: 0.82; +} + +.color-reaction-matrix__button[data-reaction='ignore'] + > .color-reaction-matrix__icon::before, +.color-reaction-matrix__button[data-reaction='ignore'] + > .color-reaction-matrix__icon::after { + display: none; +} diff --git a/src/style/_control-dock.scss b/src/style/_control-dock.scss new file mode 100644 index 0000000..aec5d2f --- /dev/null +++ b/src/style/_control-dock.scss @@ -0,0 +1,39 @@ +html > body > aside.control-dock { + --dock-hidden-translate-y: calc(100% + env(safe-area-inset-bottom, 0px) + 16px); + + position: absolute; + left: 0; + right: 0; + bottom: env(safe-area-inset-bottom, 0px); + z-index: 4; + width: min(calc(100vw - 1rem), 980px); + margin: 0 auto; + transform: translateY(0); + visibility: visible; + pointer-events: none; + transition: + opacity var(--transition-time-long), + transform var(--transition-time-long), + visibility 0s; + + > .toolbar-row, + > .info-page { + pointer-events: auto; + } + + &.menu-hidden { + opacity: 0; + visibility: hidden; + transform: translateY(var(--dock-hidden-translate-y)); + pointer-events: none; + transition: + opacity var(--transition-time-long), + transform var(--transition-time-long), + visibility 0s var(--transition-time-long); + + > .toolbar-row, + > .info-page { + pointer-events: none; + } + } +} diff --git a/src/style/_garden-prompt.scss b/src/style/_garden-prompt.scss new file mode 100644 index 0000000..6f7cf74 --- /dev/null +++ b/src/style/_garden-prompt.scss @@ -0,0 +1,126 @@ +@use 'mixins' as *; + +html > body > .canvas-container > .garden-prompt { + position: absolute; + left: 50%; + transform: translateX(-50%); + text-align: center; + pointer-events: none; + z-index: 2; + + &:empty { + display: none; + } + + &.draw-hint { + display: flex; + align-items: center; + top: calc(1.25rem + env(safe-area-inset-top)); + gap: 16px; + width: max-content; + min-height: 78px; + max-width: min(88vw, 460px); + padding: 12px 18px 12px 14px; + border: 1px solid rgb(255 255 255 / 16%); + border-radius: 8px; + background: rgb(10 12 16 / 50%); + box-shadow: + 0 14px 42px rgb(0 0 0 / 28%), + inset 0 0 0 1px rgb(255 255 255 / 5%); + backdrop-filter: blur(12px); + color: rgb(255 255 255 / 94%); + font-size: 20px; + font-weight: 400; + line-height: 1.2; + text-shadow: 0 1px 12px rgb(0 0 0 / 58%); + } + + .draw-hint-mark { + width: 128px; + height: 72px; + flex: 0 0 128px; + overflow: visible; + } + + .draw-hint-shadow, + .draw-hint-stroke { + fill: none; + stroke-linecap: round; + stroke-linejoin: round; + } + + .draw-hint-shadow { + stroke: rgb(0 0 0 / 42%); + stroke-width: 10px; + } + + .draw-hint-stroke { + stroke: color-mix(in srgb, var(--accent-color) 74%, white); + stroke-width: 5px; + stroke-dasharray: 154; + animation: draw-hint-stroke 2.4s ease-in-out infinite; + filter: drop-shadow( + 0 0 12px color-mix(in srgb, var(--accent-color) 60%, transparent) + ); + } + + .draw-hint-start { + fill: rgb(255 255 255 / 64%); + } + + .draw-hint-end { + fill: white; + stroke: color-mix(in srgb, var(--accent-color) 72%, transparent); + stroke-width: 5px; + transform-origin: 116px 42px; + animation: draw-hint-tap 2.4s ease-in-out infinite; + } + + @include on-small-screen { + &.draw-hint { + top: calc(0.75rem + env(safe-area-inset-top)); + gap: 10px; + min-height: 58px; + max-width: min(92vw, 340px); + padding: 9px 12px 9px 10px; + font-size: 16px; + } + + .draw-hint-mark { + width: 96px; + height: 54px; + flex-basis: 96px; + } + } +} + +@keyframes draw-hint-stroke { + 0%, + 18% { + stroke-dashoffset: 154; + } + + 58%, + 100% { + stroke-dashoffset: 0; + } +} + +@keyframes draw-hint-tap { + 0%, + 16% { + opacity: 0; + transform: scale(0.72); + } + + 36%, + 74% { + opacity: 1; + transform: scale(1); + } + + 100% { + opacity: 0.76; + transform: scale(0.88); + } +} diff --git a/src/style/_loading.scss b/src/style/_loading.scss new file mode 100644 index 0000000..925f915 --- /dev/null +++ b/src/style/_loading.scss @@ -0,0 +1,183 @@ +.loading-indicator { + --loading-gap: 22px; + + position: absolute; + top: 50%; + left: 50%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 3; + width: min(86vw, 380px); + transform: translate(-50%, -50%); + opacity: 0; + pointer-events: none; + transition: opacity var(--transition-time-long); + contain: layout; + + > .splash { + display: flex; + flex-direction: column; + gap: 16px; + align-items: center; + pointer-events: auto; + opacity: 1; + visibility: visible; + transition: + opacity var(--transition-time), + visibility 0s linear 0s; + + &[data-visible='false'] { + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: + opacity var(--transition-time), + visibility 0s linear var(--transition-time); + } + + > .splash-title { + margin: 0; + color: rgb(255 255 255 / 96%); + font-size: clamp(28px, 6vw, 42px); + font-weight: 700; + line-height: 1.1; + text-align: center; + letter-spacing: 0.01em; + text-shadow: + 0 2px 18px rgb(0 0 0 / 60%), + 0 0 32px rgb(255 255 255 / 10%); + } + + > .splash-description { + margin: 0; + max-width: 28ch; + color: rgb(255 255 255 / 80%); + font-size: 15px; + font-weight: 400; + line-height: 1.45; + text-align: center; + text-shadow: 0 1px 12px rgb(0 0 0 / 60%); + } + + > .start-button { + margin-top: 8px; + padding: 14px 40px; + border: 1px solid rgb(255 255 255 / 38%); + border-radius: 999px; + background: rgb(255 255 255 / 8%); + color: rgb(255 255 255 / 96%); + font-size: 16px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + cursor: pointer; + backdrop-filter: blur(6px); + box-shadow: + 0 0 24px rgb(255 255 255 / 14%), + 0 1px 6px rgb(0 0 0 / 28%); + transition: + opacity var(--transition-time), + transform var(--transition-time), + background var(--transition-time); + + &[disabled] { + opacity: 0.5; + cursor: progress; + } + + &:not([disabled]):hover, + &:not([disabled]):focus-visible { + background: rgb(255 255 255 / 16%); + transform: scale(1.04); + outline: none; + } + + &:not([disabled]):active { + transform: scale(0.98); + } + } + } + + > .loading-bar { + position: absolute; + top: calc(100% + var(--loading-gap)); + left: 0; + right: 0; + display: flex; + flex-direction: column; + gap: 18px; + align-items: center; + width: 100%; + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: + opacity var(--transition-time), + visibility 0s linear var(--transition-time); + + &[data-visible='true'] { + opacity: 1; + visibility: visible; + transition: + opacity var(--transition-time), + visibility 0s linear 0s; + } + + > .loading-status { + color: rgb(255 255 255 / 88%); + font-size: 16px; + font-weight: 400; + line-height: 1.25; + text-align: center; + text-shadow: 0 1px 12px rgb(0 0 0 / 60%); + letter-spacing: 0.01em; + min-height: 1.25em; + } + + > .loading-progress { + --loading-progress: 0%; + + position: relative; + width: 100%; + height: 3px; + overflow: hidden; + border-radius: 999px; + background: rgb(255 255 255 / 14%); + box-shadow: 0 1px 6px rgb(0 0 0 / 28%); + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: var(--loading-progress); + border-radius: inherit; + background: linear-gradient( + 90deg, + rgb(255 255 255 / 72%), + rgb(255 255 255 / 96%) + ); + box-shadow: 0 0 12px rgb(255 255 255 / 38%); + transition: width var(--transition-time-long) ease-out; + } + } + } +} + +html > body.is-loading { + .loading-indicator { + opacity: 1; + } + + .eraser-preview { + visibility: hidden; + } + + aside.control-dock { + opacity: 0; + visibility: hidden; + } +} diff --git a/src/style/_panels.scss b/src/style/_panels.scss new file mode 100644 index 0000000..28717bb --- /dev/null +++ b/src/style/_panels.scss @@ -0,0 +1,96 @@ +@use 'mixins' as *; + +html > body > aside.control-dock > .info-page { + width: min(calc(100vw - 1rem), 560px); + max-height: min(58vh, 520px); + max-height: min(58dvh, 520px); + margin: 0 auto 10px; + overflow-x: hidden; + overflow-y: auto; + border: 1px solid rgb(255 255 255 / 78%); + border-radius: 8px; + background: + linear-gradient(180deg, rgb(255 255 255 / 97%), rgb(243 247 239 / 96%)), + rgb(255 255 255); + color: rgb(24 30 27); + box-shadow: + 0 20px 54px rgb(0 0 0 / 38%), + 0 2px 12px rgb(0 0 0 / 22%); + backdrop-filter: blur(12px); + scrollbar-width: thin; + scrollbar-color: var(--main-color) transparent; + transition: + max-height var(--transition-time-long), + opacity var(--transition-time-long), + transform var(--transition-time-long), + margin-bottom var(--transition-time-long); + + &::-webkit-scrollbar-track, + &::-webkit-scrollbar { + background-color: transparent; + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background-color: var(--main-color); + border-radius: 8px; + } + + &:focus-visible { + outline: 2px solid white; + outline-offset: 3px; + } + + > section { + display: flex; + flex-direction: column; + gap: 0.85rem; + padding: var(--normal-margin); + + h1 { + margin-bottom: 0; + color: rgb(16 24 20); + font-size: 2rem; + line-height: 1.1; + } + + p { + max-width: 54ch; + margin-bottom: 0; + color: rgb(42 48 45); + font-size: 1.1rem; + line-height: 1.65; + hyphens: auto; + } + + a { + color: rgb(0 84 120); + font-weight: 400; + + &:focus-visible { + outline: 2px solid currentColor; + outline-offset: 3px; + } + } + } + + &.hidden { + max-height: 0; + margin-bottom: 0; + border-color: transparent; + opacity: 0; + pointer-events: none; + box-shadow: none; + transform: translateY(8px); + visibility: hidden; + } + + @include on-small-screen { + max-height: min(54vh, 500px); + max-height: min(54dvh, 500px); + + > section { + padding: var(--small-margin); + } + } +} diff --git a/src/style/_toolbar.scss b/src/style/_toolbar.scss new file mode 100644 index 0000000..ef7ca8c --- /dev/null +++ b/src/style/_toolbar.scss @@ -0,0 +1,4 @@ +@use 'toolbar/layout'; +@use 'toolbar/buttons'; +@use 'toolbar/garden-controls'; +@use 'toolbar/responsive'; diff --git a/src/style/common.scss b/src/style/common.scss index 8954439..23c82aa 100644 --- a/src/style/common.scss +++ b/src/style/common.scss @@ -1,6 +1,5 @@ @use 'vars'; @use 'fonts'; -@use 'mixins' as *; *, *::before, @@ -9,41 +8,34 @@ padding: 0; box-sizing: border-box; - @media (prefers-reduced-motion) { + @media (prefers-reduced-motion: reduce) { transition: none !important; animation: none !important; } } -h1, -h2, -h3, -h4, -h5, -h6 { - font-family: 'Comfortaa', sans-serif; - margin-bottom: var(--small-margin); -} - -p { - font-family: 'Open Sans', sans-serif; -} - html { height: 100%; + touch-action: manipulation; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } -.large-button { - border: none; - background-color: var(--accent-color); - cursor: pointer; - border-radius: var(--border-radius); - padding: calc(var(--small-margin) / 2) var(--small-margin); - margin: var(--small-margin) auto; - align-self: flex-end; - @include main-font(); - color: white; +body { + font-family: 'Open Sans', sans-serif; + touch-action: manipulation; +} + +.visually-hidden { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + padding: 0 !important; + overflow: hidden !important; + clip: rect(0 0 0 0) !important; + clip-path: inset(50%) !important; + white-space: nowrap !important; + border: 0 !important; } diff --git a/src/style/fonts.scss b/src/style/fonts.scss index a00d604..a4f4b3c 100644 --- a/src/style/fonts.scss +++ b/src/style/fonts.scss @@ -1,25 +1,8 @@ -/* comfortaa-regular - latin */ -@font-face { - font-family: 'Comfortaa'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: - local(''), - url('../../assets/fonts/comfortaa-v40-latin-regular.woff2') format('woff2'), - /* Chrome 26+, Opera 23+, Firefox 39+ */ - url('../../assets/fonts/comfortaa-v40-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - /* open-sans-regular - latin */ @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: 400; font-display: swap; - src: - local(''), - url('../../assets/fonts/open-sans-v34-latin-regular.woff2') format('woff2'), - /* Chrome 26+, Opera 23+, Firefox 39+ */ - url('../../assets/fonts/open-sans-v34-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ + src: url('../../assets/fonts/open-sans-v34-latin-regular.woff2') format('woff2'); } diff --git a/src/style/mixins.scss b/src/style/mixins.scss index 4ed95f5..9197605 100644 --- a/src/style/mixins.scss +++ b/src/style/mixins.scss @@ -1,6 +1,3 @@ -@use 'sass:math'; -@use 'sass:color'; - $breakpoint-width: 600px !default; @mixin on-small-screen() { @@ -9,131 +6,8 @@ $breakpoint-width: 600px !default; } } -@mixin on-large-screen() { - @media (min-width: $breakpoint-width) { +@mixin on-mobile-input() { + @media (max-width: ($breakpoint-width - 1px)), (hover: none) and (pointer: coarse) { @content; } } - -@mixin title-fragment-link() { - position: relative; - - &:before { - content: '#'; - position: absolute; - left: -0.5ch; - top: 50%; - opacity: 0; - transform: translateX(-100%) translateY(-50%); - transition: opacity var(--transition-time); - } - - &:hover:before { - opacity: 0.5; - } -} - -@mixin center-children() { - display: flex; - align-items: center; - justify-content: center; -} - -@mixin absolute-center() { - position: absolute; - left: 50%; - top: 50%; - transform: translateX(-50%) translateY(-50%); -} - -@mixin blurred-background($color: transparent) { - background-color: color.adjust($color, $alpha: -0.66); - backdrop-filter: blur(var(--blur-radius)); -} - -@mixin square($size) { - width: $size; - height: $size; -} - -@mixin title-font() { - font: - 400 3rem 'Comfortaa', - sans-serif; - color: var(--normal-text-color); - line-height: 1; - - @include on-small-screen { - font-size: 3rem; - line-height: 1.1; - } -} - -@mixin sub-title-font() { - font: - 400 1.75rem 'Comfortaa', - sans-serif; - color: var(--normal-text-color); - hyphens: auto; -} - -@mixin main-font() { - font: - 400 1.1rem 'Open Sans', - sans-serif; - color: var(--normal-text-color); - line-height: 1.8; - hyphens: auto; -} - -@mixin special-text-font() { - font: - 400 1rem 'Open Sans', - sans-serif; - color: var(--special-text-color); - hyphens: auto; - font-style: italic; -} - -@mixin link { - $border-shift: 10px; - $line-width: 2px; - - @include special-text-font(); - cursor: pointer; - position: relative; - display: inline-block; - overflow: hidden; - - padding: 0 3px $line-width 0; - - &:before, - &:after { - content: ''; - display: block; - position: absolute; - bottom: 0; - } - - &:before { - width: calc(100% + #{$border-shift}); - border-bottom: $line-width dashed var(--accent-color); - transition: transform var(--transition-time); - } - - &:after { - width: 100%; - height: $line-width; - background: linear-gradient( - 90deg, - var(--card-color) 0, - transparent 4px, - transparent calc(100% - 4px), - var(--card-color) 100% - ); - } - - &:hover:before { - transform: translateX(-$border-shift); - } -} diff --git a/src/style/toolbar/_buttons.scss b/src/style/toolbar/_buttons.scss new file mode 100644 index 0000000..198ca47 --- /dev/null +++ b/src/style/toolbar/_buttons.scss @@ -0,0 +1,161 @@ +@use 'shared' as *; + +.buttons { + grid-area: buttons; + display: flex; + flex-wrap: nowrap; + align-items: center; + justify-content: center; + justify-self: center; + gap: 4px; + width: fit-content; + max-width: 100%; + min-width: 0; + + > button, + > .audio-control > button { + position: relative; + width: 44px; + height: 44px; + flex: 1 1 44px; + max-width: 54px; + min-width: 0; + @include toolbar-control-surface(transparent, rgb(255 255 255 / 9%)); + + &::after { + content: ''; + position: absolute; + inset: 0; + z-index: 1; + width: 20px; + height: 20px; + margin: auto; + background-color: rgb(245 250 244 / 76%); + mask-position: center; + mask-repeat: no-repeat; + mask-size: contain; + transition: + background-color var(--transition-time), + transform var(--transition-time); + } + + &:hover::after { + transform: scale(1.08); + } + + &.active { + border-color: color-mix(in srgb, var(--accent-color) 55%, white 15%); + background: color-mix(in srgb, var(--accent-color) 30%, transparent); + } + + &.active::after { + background-color: white; + } + + @each $class, $icon in $toolbar-icons { + &.#{$class}::after { + mask-image: url('../../../assets/icons/#{$icon}.svg'); + } + } + + &.full-screen-toggle.active::after { + mask-image: url('../../../assets/icons/minimize.svg'); + } + + &.sound.muted::before { + content: ''; + position: absolute; + inset: 0; + z-index: 2; + width: 2px; + height: 28px; + margin: auto; + border-radius: 999px; + background: white; + transform: rotate(-45deg); + transform-origin: center; + } + + &.sound.muted::after { + background-color: rgb(255 255 255 / 46%); + } + } + + > .audio-control { + display: flex; + align-items: center; + width: 132px; + height: 44px; + flex: 2 1 132px; + max-width: 150px; + min-width: 0; + padding-right: 10px; + @include toolbar-control-surface(rgb(255 255 255 / 4%), rgb(255 255 255 / 7%)); + + > button { + flex: 0 0 42px; + min-width: 42px; + border-color: transparent; + + &:focus-visible { + outline-offset: -4px; + } + } + + > .volume-control { + --range-progress: var(--volume-progress, 42%); + --range-track-height: 4px; + --range-fill: color-mix(in srgb, var(--accent-color) 62%, white 8%); + --range-empty: rgb(255 255 255 / 18%); + --range-track-shadow: + inset 0 1px 1px rgb(0 0 0 / 24%), 0 1px 0 rgb(255 255 255 / 8%); + --range-thumb-width: 12px; + --range-thumb-height: 12px; + --range-thumb-border: 2px solid rgb(13 18 24); + --range-thumb-radius: 50%; + --range-thumb-background: rgb(245 250 244); + --range-thumb-shadow: 0 0 0 1px rgb(255 255 255 / 46%), 0 3px 8px rgb(0 0 0 / 28%); + --range-thumb-hover-shadow: + 0 0 0 1px rgb(255 255 255 / 56%), + 0 0 0 5px color-mix(in srgb, var(--accent-color) 25%, transparent), + 0 4px 10px rgb(0 0 0 / 34%); + --range-thumb-hover-transform: scale(1.08); + --range-focus-outline-offset: -4px; + + position: relative; + display: grid; + align-items: center; + height: 44px; + flex: 1 1 auto; + min-width: 0; + padding-left: 3px; + cursor: ew-resize; + opacity: 0.96; + transition: opacity var(--transition-time); + + &.muted { + opacity: 0.56; + } + } + + > .volume-control input[type='range'] { + @include toolbar-range-input(); + } + } + + > .export-status { + flex: 0 1 140px; + min-height: 20px; + max-width: 140px; + overflow: hidden; + color: rgb(255 255 255 / 82%); + font-size: 13px; + line-height: 1.2; + text-overflow: ellipsis; + white-space: nowrap; + + &:empty { + display: none; + } + } +} diff --git a/src/style/toolbar/_garden-controls.scss b/src/style/toolbar/_garden-controls.scss new file mode 100644 index 0000000..4dca516 --- /dev/null +++ b/src/style/toolbar/_garden-controls.scss @@ -0,0 +1,148 @@ +@use 'shared' as *; + +.garden-controls { + grid-area: swatches; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + min-width: 0; + padding: 0 4px; + + > .swatches { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + min-height: 58px; + padding: 6px 10px; + + > .color-swatch { + position: relative; + width: 44px; + height: 44px; + border: 2px solid rgb(255 255 255 / 54%); + border-radius: 50%; + box-shadow: + inset 0 0 0 1px rgb(0 0 0 / 16%), + 0 3px 10px rgb(0 0 0 / 22%); + + &:hover { + transform: translateY(-2px); + } + + &.active { + outline: 2px solid rgb(255 255 255 / 96%); + outline-offset: 3px; + box-shadow: + inset 0 0 0 1px rgb(0 0 0 / 14%), + 0 0 0 7px color-mix(in srgb, var(--accent-color) 52%, transparent), + 0 7px 18px rgb(0 0 0 / 26%); + } + } + + > .eraser-size-control, + > .mirror-segment-control { + --control-thumb-hover-transform: scale(1.03); + --control-thumb-radius: 50%; + --control-thumb-transform: none; + --range-progress: var(--control-progress); + --range-track-height: 7px; + --range-fill: rgb(var(--control-rgb) / 72%); + --range-empty: rgb(255 255 255 / 24%); + --range-track-shadow: inset 0 1px 2px rgb(0 0 0 / 24%); + --range-thumb-width: var(--control-thumb-width); + --range-thumb-height: var(--control-thumb-height); + --range-thumb-border: 2px solid rgb(255 255 255 / 92%); + --range-thumb-radius: var(--control-thumb-radius); + --range-thumb-background: var(--control-thumb-background); + --range-thumb-shadow: + inset 0 1px 2px rgb(255 255 255 / 22%), 0 4px 12px rgb(0 0 0 / 30%); + --range-thumb-hover-shadow: + inset 0 1px 2px rgb(255 255 255 / 22%), 0 0 0 4px rgb(var(--control-rgb) / 22%), + 0 5px 14px rgb(0 0 0 / 34%); + --range-thumb-hover-transform: var(--control-thumb-hover-transform); + --range-thumb-transform: var(--control-thumb-transform); + --range-thumb-transition: + box-shadow var(--transition-time), height var(--transition-time), + margin-top var(--transition-time), transform var(--transition-time), + width var(--transition-time); + + position: relative; + display: grid; + align-items: center; + width: 184px; + height: 46px; + flex: 0 0 184px; + padding: 0 12px; + overflow: hidden; + border: 1px solid rgb(255 255 255 / 14%); + border-radius: 8px; + background: linear-gradient(180deg, rgb(255 255 255 / 9%), rgb(255 255 255 / 4%)); + box-shadow: + inset 0 0 0 1px rgb(255 255 255 / 6%), + 0 3px 10px rgb(0 0 0 / 18%); + cursor: ew-resize; + transition: + border-color var(--transition-time), + background-color var(--transition-time), + box-shadow var(--transition-time), + transform var(--transition-time); + + &:hover { + border-color: rgb(255 255 255 / 24%); + transform: translateY(-2px); + } + + &.active { + border-color: rgb(var(--control-rgb) / 72%); + background-color: rgb(var(--control-rgb) / 11%); + box-shadow: + inset 0 0 0 1px rgb(255 255 255 / 10%), + 0 0 0 5px rgb(var(--control-rgb) / 28%), + 0 6px 15px rgb(0 0 0 / 22%); + } + + input[type='range'] { + @include toolbar-range-input(); + } + } + + > .eraser-size-control { + --control-progress: var(--eraser-progress, 33%); + --control-rgb: 255 140 117; + --control-thumb-background: + linear-gradient( + 110deg, + transparent 0 12%, + rgb(255 255 255 / 44%) 13% 20%, + transparent 21% 100% + ), + linear-gradient( + 90deg, + #ff8fa3 0 52%, + rgb(54 46 51 / 78%) 53% 56%, + #f5eee5 57% 100% + ); + --control-thumb-height: calc(21px * var(--eraser-control-scale, 1)); + --control-thumb-hover-transform: rotate(-10deg) scale(1.03); + --control-thumb-radius: calc(6px * var(--eraser-control-scale, 1)); + --control-thumb-transform: rotate(-10deg); + --control-thumb-width: calc(34px * var(--eraser-control-scale, 1)); + } + + > .mirror-segment-control { + --control-progress: var(--mirror-progress, 0%); + --control-rgb: 148 233 203; + --control-thumb-background: + radial-gradient(circle, white 0 3px, rgb(9 20 18 / 78%) 3.5px 8px), + repeating-conic-gradient( + from -90deg, + rgb(218 255 241) 0 8deg, + rgb(8 22 19 / 94%) 8deg var(--mirror-angle, 360deg) + ); + --control-thumb-height: 44px; + --control-thumb-width: 44px; + } + } +} diff --git a/src/style/toolbar/_layout.scss b/src/style/toolbar/_layout.scss new file mode 100644 index 0000000..f2c8930 --- /dev/null +++ b/src/style/toolbar/_layout.scss @@ -0,0 +1,173 @@ +@use 'shared' as *; + +.toolbar-row { + --toolbar-background-opacity: 0%; + --toolbar-background-strength: 0; + --toolbar-divider-space: clamp(6px, 1.8vw, 14px); + --toolbar-top-max-width: 594px; + --vibe-button-hit-size: 64px; + + display: grid; + grid-template-areas: + 'previous controls next' + 'previous divider next' + 'previous buttons next'; + grid-template-columns: + var(--vibe-button-hit-size) + minmax(0, var(--toolbar-top-max-width)) + var(--vibe-button-hit-size); + align-items: stretch; + justify-content: center; + width: fit-content; + max-width: 100%; + margin: 0 auto; + padding-inline: clamp(8px, 1.4vw, 14px); + column-gap: 0; + row-gap: 0; + border-radius: 12px; + color: rgb(245 250 244 / 92%); + background-color: rgb(5 8 13 / var(--toolbar-background-opacity)); + box-shadow: + inset 0 0 0 1px rgb(255 255 255 / calc(var(--toolbar-background-strength) * 16%)), + inset 0 1px 0 rgb(255 255 255 / calc(var(--toolbar-background-strength) * 7%)), + 0 14px 34px rgb(0 0 0 / calc(var(--toolbar-background-strength) * 28%)); + backdrop-filter: blur(calc(var(--toolbar-background-strength) * 18px)) + brightness(calc(1 - var(--toolbar-background-strength) * 0.38)) + saturate(calc(1 - var(--toolbar-background-strength) * 0.18)); + font-size: 13px; + font-weight: 400; + line-height: 1; + transition: + backdrop-filter var(--transition-time-long), + background-color var(--transition-time-long), + box-shadow var(--transition-time-long); + + &::after { + content: ''; + grid-area: divider; + align-self: center; + justify-self: center; + width: min(100%, var(--toolbar-top-max-width)); + height: 1px; + margin-block: var(--toolbar-divider-space); + background: rgb(255 255 255 / 12%); + } + + button { + min-width: 44px; + min-height: 44px; + border: 0; + font: inherit; + cursor: pointer; + @include toolbar-button-transition(); + + &:disabled { + cursor: progress; + opacity: 0.58; + } + + &:focus-visible { + outline: 2px solid white; + outline-offset: 2px; + } + } + + > .toolbar-shell { + grid-area: controls; + display: grid; + grid-template-areas: 'swatches'; + grid-template-columns: minmax(0, 1fr); + align-items: center; + justify-content: center; + justify-self: center; + width: min(100%, var(--toolbar-top-max-width)); + min-width: 0; + padding: 8px 9px; + } + + > .vibe-button { + --vibe-button-surface-inset-block: 10px; + --vibe-button-surface-inset-inline: 8px; + --vibe-chevron-size: 22px; + --vibe-chevron-stroke: 4px; + + position: relative; + isolation: isolate; + display: grid; + place-items: center; + width: var(--vibe-button-hit-size); + height: auto; + min-height: 72px; + flex: 0 0 auto; + padding: 0; + border-radius: 0; + background: transparent; + color: rgb(255 255 255 / 88%); + font-size: 0; + line-height: 1; + + &::after { + content: ''; + position: absolute; + z-index: 0; + inset: var(--vibe-button-surface-inset-block) + var(--vibe-button-surface-inset-inline); + border-radius: 8px; + background: rgb(255 255 255 / calc(9% + var(--toolbar-background-strength) * 10%)); + box-shadow: + inset 0 0 0 1px rgb(255 255 255 / 18%), + 0 8px 18px rgb(0 0 0 / calc(var(--toolbar-background-strength) * 22%)); + transition: + background var(--transition-time), + box-shadow var(--transition-time), + opacity var(--transition-time); + } + + &::before { + content: ''; + position: absolute; + z-index: 1; + top: 50%; + left: 50%; + width: var(--vibe-chevron-size); + height: var(--vibe-chevron-size); + border-color: currentColor; + border-style: solid; + border-width: 0 0 var(--vibe-chevron-stroke) var(--vibe-chevron-stroke); + filter: drop-shadow(0 1px 3px rgb(0 0 0 / 70%)); + transform: translate(-35%, -50%) rotate(45deg); + } + + &.next-vibe::before { + border-width: var(--vibe-chevron-stroke) var(--vibe-chevron-stroke) 0 0; + transform: translate(-65%, -50%) rotate(45deg); + } + + &:hover { + color: white; + } + + &:hover::after { + background: color-mix(in srgb, var(--accent-color) 34%, rgb(255 255 255 / 18%)); + box-shadow: + inset 0 0 0 1px rgb(255 255 255 / 28%), + 0 10px 22px rgb(0 0 0 / calc(var(--toolbar-background-strength) * 30%)); + } + + &.previous-vibe:hover { + transform: translateX(-2px); + } + + &.next-vibe:hover { + transform: translateX(2px); + } + } + + > .previous-vibe { + grid-area: previous; + } + + > .next-vibe { + grid-area: next; + } +} diff --git a/src/style/toolbar/_responsive.scss b/src/style/toolbar/_responsive.scss new file mode 100644 index 0000000..2aeba6c --- /dev/null +++ b/src/style/toolbar/_responsive.scss @@ -0,0 +1,157 @@ +@use '../mixins' as *; + +.toolbar-row { + @include on-small-screen { + --toolbar-divider-space: 4px; + --toolbar-top-max-width: 329px; + --vibe-button-hit-size: 44px; + + grid-template-areas: + 'previous controls next' + '. divider .' + 'buttons buttons buttons'; + width: 100%; + padding-inline: 4px; + column-gap: 0; + row-gap: 0; + + > .vibe-button { + --vibe-button-surface-inset-block: 5px; + --vibe-button-surface-inset-inline: 3px; + --vibe-chevron-size: 17px; + --vibe-chevron-stroke: 3px; + + width: var(--vibe-button-hit-size); + min-height: 44px; + } + + > .toolbar-shell { + padding: 4px; + } + + > nav.buttons { + justify-self: stretch; + justify-content: space-between; + gap: clamp(1px, 0.55vw, 2px); + width: auto; + max-width: none; + margin-inline: -4px; + + > button { + width: auto; + height: 38px; + flex: 1 1 clamp(28px, 8vw, 38px); + max-width: 38px; + min-height: 38px; + + &::after { + width: 17px; + height: 17px; + } + } + + > .audio-control { + width: auto; + height: 38px; + flex: 2 1 clamp(58px, 18vw, 118px); + max-width: 118px; + padding-right: clamp(4px, 1.8vw, 9px); + + > button { + width: auto; + flex: 1 1 clamp(28px, 8vw, 38px); + min-width: 0; + } + + > .volume-control { + height: 38px; + } + } + + > .export-status { + flex-basis: 0; + max-width: 0; + text-align: center; + } + } + + > .toolbar-shell > .garden-controls { + padding: 2px 4px; + + > .swatches { + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + justify-items: center; + justify-content: stretch; + width: 100%; + min-width: 0; + min-height: 48px; + flex: 1 1 100%; + padding: 3px 5px; + column-gap: 6px; + row-gap: 6px; + + > .color-swatch { + width: 38px; + height: 38px; + min-width: 38px; + min-height: 38px; + + grid-column: span 2; + } + + > .eraser-size-control, + > .mirror-segment-control { + justify-self: stretch; + width: 100%; + min-width: 0; + height: 38px; + padding: 0 7px; + } + + > .eraser-size-control { + grid-column: 1 / span 3; + } + + > .mirror-segment-control { + --control-thumb-height: 34px; + --control-thumb-width: 34px; + + grid-column: 4 / span 3; + } + } + } + } +} + +@media (prefers-reduced-motion: reduce) { + .toolbar-row { + > .vibe-button.previous-vibe:hover, + > .vibe-button.next-vibe:hover, + > .toolbar-shell > .garden-controls > .swatches > .color-swatch:hover, + > .toolbar-shell > .garden-controls > .swatches > .eraser-size-control:hover, + > .toolbar-shell > .garden-controls > .swatches > .mirror-segment-control:hover { + transform: none; + } + + > nav.buttons > button:hover::after, + > nav.buttons > .audio-control > button:hover::after { + transform: none; + } + + > nav.buttons > .audio-control > .volume-control input[type='range'] { + &::-webkit-slider-thumb:hover { + transform: none; + } + } + + > .toolbar-shell > .garden-controls > .swatches { + > .eraser-size-control input[type='range'], + > .mirror-segment-control input[type='range'] { + &::-webkit-slider-thumb:hover { + transform: var(--range-thumb-transform, none); + } + } + } + } +} diff --git a/src/style/toolbar/_shared.scss b/src/style/toolbar/_shared.scss new file mode 100644 index 0000000..ed477f2 --- /dev/null +++ b/src/style/toolbar/_shared.scss @@ -0,0 +1,104 @@ +$toolbar-icons: ( + info: 'info', + full-screen-toggle: 'maximize', + settings: 'settings', + sound: 'sound', + export-4k: 'download', + restart: 'restart', +); + +@mixin toolbar-button-transition() { + transition: + background-color var(--transition-time), + border-color var(--transition-time), + color var(--transition-time), + box-shadow var(--transition-time), + opacity var(--transition-time), + transform var(--transition-time); +} + +@mixin toolbar-control-surface($background, $hover-background) { + border: 1px solid transparent; + border-radius: 8px; + background: $background; + transition: + border-color var(--transition-time), + background-color var(--transition-time), + box-shadow var(--transition-time), + opacity var(--transition-time); + + &:hover { + border-color: rgb(255 255 255 / 10%); + background: $hover-background; + } +} + +@mixin toolbar-range-track() { + height: var(--range-track-height); + border: var(--range-track-border, 0); + border-radius: 999px; + background: linear-gradient( + 90deg, + var(--range-fill) 0 var(--range-progress), + var(--range-empty) var(--range-progress) 100% + ); + box-shadow: var(--range-track-shadow); + cursor: ew-resize; +} + +@mixin toolbar-range-thumb() { + width: var(--range-thumb-width); + height: var(--range-thumb-height); + border: var(--range-thumb-border); + border-radius: var(--range-thumb-radius); + background: var(--range-thumb-background); + box-shadow: var(--range-thumb-shadow); + cursor: ew-resize; + transform: var(--range-thumb-transform, none); +} + +@mixin toolbar-range-input() { + position: relative; + z-index: 1; + width: 100%; + height: 100%; + appearance: none; + background: transparent; + cursor: ew-resize; + outline: none; + touch-action: pan-y; + + &:focus-visible { + border-radius: 8px; + outline: 2px solid white; + outline-offset: var(--range-focus-outline-offset, 2px); + } + + &::-webkit-slider-runnable-track { + @include toolbar-range-track(); + } + + &::-webkit-slider-thumb { + @include toolbar-range-thumb(); + margin-top: calc((var(--range-track-height) - var(--range-thumb-height)) / 2); + appearance: none; + transition: var( + --range-thumb-transition, + box-shadow var(--transition-time), + transform var(--transition-time) + ); + } + + &::-webkit-slider-thumb:hover { + box-shadow: var(--range-thumb-hover-shadow, var(--range-thumb-shadow)); + transform: var(--range-thumb-hover-transform, var(--range-thumb-transform, none)); + } + + &::-moz-range-track { + @include toolbar-range-track(); + } + + &::-moz-range-thumb { + @include toolbar-range-thumb(); + } +} diff --git a/src/style/vars.scss b/src/style/vars.scss index aa832ca..319d82e 100644 --- a/src/style/vars.scss +++ b/src/style/vars.scss @@ -1,33 +1,8 @@ -@use 'mixins' as *; - :root { --transition-time: 200ms; --transition-time-long: 350ms; - --line-width: 4px; - --line-height: 1.125rem; - --accent-color: rgb(6.39851188659668, 70.28645324707031, 102.23043060302734); - --very-light-text-color: #ffffff; + --accent-color: rgb(255 93 162); --main-color: #aaa; - --normal-text-color: #31343f; - --blurred-card-color: transparent; - --blur-radius: 12px; - --special-text-color: var(--accent-color); - --border-radius: 0.6rem; - - --large-margin: 4.6rem; --normal-margin: 2rem; --small-margin: 1rem; - --shadow: 0 0 5px 2px rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.2); - --icon-size: 2.5rem; - --large-icon-size: 3.75rem; - --body-width: min(80%, 60rem); -} - -@include on-small-screen { - :root { - --body-width: 90%; - --large-margin: 2.8rem; - --normal-margin: 2rem; - --icon-size: 2rem; - } }