Deploy web Studio / deploy (push) Successful in 1m3s
With Windows "Animation effects" off, browsers report prefers-reduced-motion, and the viewer
turned every animation off and hid the animation button, so presses, belts and robot arms
stood still in Brave and Firefox with no way to start them. Now that preference only picks
the starting mode ("off"); the button stays (its tooltip says why it is off) and the
viewer's choice wins and is remembered. The CSS still-picture rules apply only until the
viewer turns animation on (.iso-anim-on).
Also: switching light -> off -> smooth redraws the scene so the animations paused by script
run again (they ignore CSS play states).
Checks: web 93/93 (reduced motion: starts still, one click runs it, kept after reload),
Studio 109/109, 70 Rust tests. Tried in real Brave (system setting) and Firefox.
195 lines
13 KiB
CSS
195 lines
13 KiB
CSS
/* isokit-view.css: structure of the isokit viewer (stage, toolbar, tooltip,
|
|
faces, tags, selection, animation, full screen). Colours come from the
|
|
theme (palette variables --iso-*); this file holds no scene colours.
|
|
Ported from the CNX Vinyl / CNX Scuba / Mooban views. */
|
|
|
|
.iso-wrap { position: relative; color: var(--iso-tipfg); }
|
|
|
|
/* stage, toolbar, tooltip, note */
|
|
.iso-stage {
|
|
position: relative; width: 100%; aspect-ratio: 16 / 10; min-height: 340px; max-height: 86vh;
|
|
overflow: hidden; border-radius: 8px; border: 1px solid var(--iso-btnline);
|
|
background:
|
|
radial-gradient(1px 1px at 12% 18%, var(--iso-star) 50%, transparent 51%),
|
|
radial-gradient(1px 1px at 33% 9%, var(--iso-star) 50%, transparent 51%),
|
|
radial-gradient(1px 1px at 58% 14%, var(--iso-star) 50%, transparent 51%),
|
|
radial-gradient(1.2px 1.2px at 81% 7%, var(--iso-star) 50%, transparent 51%),
|
|
radial-gradient(1px 1px at 91% 22%, var(--iso-star) 50%, transparent 51%),
|
|
linear-gradient(180deg, var(--iso-sky1), var(--iso-sky2));
|
|
}
|
|
.iso-svg { display: block; width: 100%; height: 100%; touch-action: pan-y; cursor: grab; user-select: none; -webkit-user-select: none; outline: none; }
|
|
/* its own compositor layer: pan and zoom gestures move it by a transform (no repaint) until they end;
|
|
drawn past its box so the ground just outside the view is there while panning (the stage clips) */
|
|
.iso-svg { will-change: transform; transform-origin: 0 0; overflow: visible; }
|
|
.iso-dragging .iso-svg { cursor: grabbing; }
|
|
.iso-svg:focus-visible { outline: 2px solid var(--iso-accent); outline-offset: -2px; }
|
|
.iso-bar {
|
|
position: absolute; top: 8px; left: 8px; right: 8px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
|
|
pointer-events: none; font: 13px/1.2 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
}
|
|
.iso-bar > *, .iso-bar button { pointer-events: auto; }
|
|
.iso-bar button, .iso-compass {
|
|
background: var(--iso-btn); color: var(--iso-btnfg); border: 1px solid var(--iso-btnline); border-radius: 5px;
|
|
padding: 5px 10px; font: inherit; cursor: pointer; min-width: 32px; min-height: 30px;
|
|
}
|
|
.iso-bar button:hover { border-color: var(--iso-accent); }
|
|
.iso-bar button:focus-visible { outline: 2px solid var(--iso-accent); outline-offset: 1px; }
|
|
.iso-seg { display: inline-flex; }
|
|
.iso-seg button { border-radius: 0; }
|
|
.iso-seg button:first-child { border-radius: 5px 0 0 5px; }
|
|
.iso-seg button:last-child { border-radius: 0 5px 5px 0; }
|
|
.iso-seg button + button { border-left: 0; }
|
|
.iso-seg button[aria-pressed=true] { background: var(--iso-btnon); border-color: var(--iso-accent); color: var(--iso-btnfg); font-weight: 600; }
|
|
.iso-compass { display: inline-flex; align-items: center; gap: 2px; padding: 3px 7px; cursor: default; }
|
|
.iso-compass svg { width: 20px; height: 20px; }
|
|
.iso-compass path { fill: var(--iso-accent); }
|
|
.iso-compass b { font-size: 11px; }
|
|
.iso-tip {
|
|
position: absolute; left: 0; top: 0; z-index: 3; max-width: min(330px, calc(100% - 12px)); padding: 8px 10px; pointer-events: none;
|
|
will-change: transform; contain: layout style;
|
|
background: var(--iso-tipbg); color: var(--iso-tipfg); border: 1px solid var(--iso-tipline); border-radius: 6px;
|
|
font: 13px/1.4 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; box-shadow: 0 4px 14px rgba(0, 0, 0, .28);
|
|
}
|
|
.iso-tip b { display: block; font-weight: 600; margin-bottom: 2px; }
|
|
.iso-tip span { display: block; color: var(--iso-tipmute); font-size: 12px; }
|
|
.iso-tip table { margin-top: 4px; border-collapse: collapse; font-size: 12px; }
|
|
.iso-tip th { text-align: left; font-weight: 400; color: var(--iso-tipmute); padding: 1px 10px 1px 0; }
|
|
.iso-tip td { text-align: right; font-variant-numeric: tabular-nums; }
|
|
.iso-tip[hidden], .iso-wrap [hidden] { display: none !important; }
|
|
.iso-note { margin: 6px 2px 0; font: 12px/1.4 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; color: var(--iso-mute); }
|
|
.iso-err {
|
|
position: absolute; left: 8px; right: 8px; bottom: 8px; z-index: 4; padding: 8px 10px; border-radius: 6px;
|
|
background: var(--iso-warnbg); color: var(--iso-warnfg); border: 1px solid var(--iso-warnline);
|
|
font: 13px/1.4 ui-monospace, "Cascadia Mono", Consolas, monospace;
|
|
}
|
|
.iso-err b { display: block; font: 600 13px/1.4 system-ui, sans-serif; }
|
|
.iso-err small { display: block; opacity: .8; margin-top: 3px; }
|
|
.iso-loading .iso-svg { opacity: .92; }
|
|
|
|
/* faces: material colour --b mixed towards the shade per face direction */
|
|
.iso-svg .iso-t { fill: var(--b, #888); }
|
|
.iso-svg .iso-l { fill: color-mix(in srgb, var(--b, #888) var(--iso-kl), var(--iso-shade)); }
|
|
.iso-svg .iso-c { fill: color-mix(in srgb, var(--b, #888) var(--iso-kc), var(--iso-shade)); }
|
|
.iso-svg .iso-r { fill: color-mix(in srgb, var(--b, #888) var(--iso-kr), var(--iso-shade)); }
|
|
.iso-sprites path, .iso-sprites ellipse, .iso-sprites circle { stroke: var(--iso-edge); stroke-width: var(--iso-edgew); stroke-linejoin: round; }
|
|
.iso-ground path, .iso-ground ellipse { stroke: none; }
|
|
@supports not (color: color-mix(in srgb, red 50%, blue)) {
|
|
.iso-svg .iso-l, .iso-svg .iso-c, .iso-svg .iso-r { fill: var(--b, #888); }
|
|
.iso-svg .iso-r { fill-opacity: .82; }
|
|
}
|
|
|
|
/* ghosting (levels above a building) */
|
|
.iso-svg .iso-dim { opacity: .4; }
|
|
html[data-theme=light] .iso-svg .iso-dim { opacity: .5; }
|
|
.iso-svg .iso-dim .iso-a { animation: none !important; }
|
|
|
|
/* tags (counter-scaled by --iso-k so they keep their screen size) */
|
|
.iso-tag path { fill: var(--iso-tagbg); stroke: var(--iso-tagline); stroke-width: 1; }
|
|
.iso-tag text { fill: var(--iso-tagfg); font: 600 10.5px/1 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; text-anchor: middle; }
|
|
.iso-signtag path { fill: var(--iso-signbg); stroke: var(--iso-signline); }
|
|
.iso-signtag text { fill: var(--iso-signfg); }
|
|
.iso-warntag path { fill: var(--iso-warnbg); stroke: var(--iso-warnline); }
|
|
.iso-warntag text { fill: var(--iso-warnfg); }
|
|
.iso-tag { cursor: default; }
|
|
.iso-svg .iso-hide { display: none; }
|
|
.iso-svg .iso-ghost { opacity: .38; }
|
|
|
|
/* hover and selection; .iso-osel marks the object selected in the Studio */
|
|
.iso-svg .iso-hov > path, .iso-svg .iso-hov > ellipse, .iso-svg path.iso-hov { stroke: var(--iso-hov); stroke-width: .8; }
|
|
.iso-svg .iso-sel > path, .iso-svg .iso-sel > ellipse, .iso-svg .iso-sel > g > path, .iso-svg path.iso-sel { stroke: var(--iso-sel); stroke-width: 1.1; }
|
|
.iso-svg .iso-osel path, .iso-svg .iso-osel ellipse, .iso-svg .iso-osel circle { stroke: var(--iso-sel); stroke-width: 1.2; }
|
|
.iso-svg .iso-moving { opacity: .75; }
|
|
|
|
/* animation: CSS only; paused off-screen (iso-off), when stopped (iso-still) and for reduced motion */
|
|
.iso-svg .iso-fan { transform-box: fill-box; transform-origin: center; animation: iso-spin 1.1s linear infinite; }
|
|
.iso-svg .iso-puff, .iso-svg .iso-puff2, .iso-svg .iso-smoke { transform-box: fill-box; transform-origin: center; animation: iso-puff 3.2s ease-out infinite; opacity: 0; pointer-events: none; stroke: none; }
|
|
.iso-svg .iso-puff, .iso-svg .iso-puff2 { fill: var(--iso-steam); }
|
|
.iso-svg .iso-smoke { fill: var(--iso-smoke); }
|
|
.iso-svg .iso-glint { animation: iso-glint 7s ease-in-out infinite; display: none; }
|
|
html[data-theme=light] .iso-svg .iso-glint { display: inline; }
|
|
.iso-svg .iso-disc { animation: iso-move 16s linear infinite; }
|
|
.iso-svg .iso-walk { animation: iso-shuttle 70s ease-in-out infinite; }
|
|
.iso-svg .iso-shuttle { animation: iso-shuttle 48s ease-in-out infinite; }
|
|
.iso-svg .iso-drive { animation: iso-drive 16s linear infinite; }
|
|
.iso-svg .iso-ledr, .iso-svg .iso-ledo { animation: iso-blink 2.4s ease-in-out infinite; }
|
|
/* fish: each turns round its pond's middle (the box is centred there), at its own speed */
|
|
.iso-svg .iso-swim { transform-box: fill-box; transform-origin: center; animation: iso-spin 20s linear infinite; }
|
|
.iso-svg .iso-rip { transform-box: fill-box; transform-origin: center; animation: iso-rip 5.1s ease-out infinite; }
|
|
/* conveyors: the goods move one spacing per loop and start again, the first fading in, the last out;
|
|
presses go down once per item */
|
|
.iso-svg .iso-ride { animation: iso-move 2s linear infinite; }
|
|
.iso-svg .iso-ridein { animation: iso-fadein 2s linear infinite; }
|
|
.iso-svg .iso-rideout { animation: iso-fadeout 2s linear infinite; }
|
|
.iso-svg .iso-rideio { animation: iso-fadeio 2s linear infinite; }
|
|
.iso-svg .iso-ram { animation: iso-ram 2s ease-in-out infinite; }
|
|
/* robot arms: a flipbook of 16 poses, each shown for a sixteenth of the cycle (delays set the turn);
|
|
unanimated (reduced motion, ghosted) only the first */
|
|
.iso-svg .iso-flip { visibility: hidden; animation: iso-flip16 4s step-end infinite; }
|
|
.iso-svg .iso-flip.iso-pose0 { visibility: visible; }
|
|
.iso-svg .iso-park { display: none; }
|
|
/* paused off-screen (iso-off), when stopped (iso-still), with animation off and during pan/zoom gestures */
|
|
.iso-off .iso-a, .iso-still .iso-a, .iso-off .iso-drive, .iso-still .iso-drive, .iso-off .iso-ledr, .iso-off .iso-ledo, .iso-still .iso-ledr, .iso-still .iso-ledo,
|
|
.iso-anim-off .iso-a, .iso-anim-off .iso-drive, .iso-anim-off .iso-ledr, .iso-anim-off .iso-ledo,
|
|
.iso-gesture .iso-a, .iso-gesture .iso-drive, .iso-gesture .iso-ledr, .iso-gesture .iso-ledo { animation-play-state: paused !important; }
|
|
.iso-still .iso-park, .iso-anim-off .iso-park { display: inline; }
|
|
.iso-animbtn { min-width: 30px; font-size: 13px; }
|
|
.iso-bar [hidden] { display: none !important; }
|
|
@keyframes iso-spin { to { transform: rotate(360deg); } }
|
|
@keyframes iso-puff {
|
|
0% { transform: translate(0, 0) scale(.35); opacity: 0; }
|
|
12% { opacity: .75; }
|
|
100% { transform: translate(5px, -28px) scale(2); opacity: 0; }
|
|
}
|
|
@keyframes iso-rip { 0% { transform: scale(.2); opacity: 0; } 15% { opacity: .9; } 100% { transform: scale(1.5); opacity: 0; } }
|
|
@keyframes iso-glint { 0%, 84%, 100% { opacity: 0; } 91% { opacity: .9; } }
|
|
@keyframes iso-blink { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
|
|
@keyframes iso-fadein { 0% { opacity: 0; } 35%, 100% { opacity: 1; } }
|
|
@keyframes iso-fadeout { 0%, 65% { opacity: 1; } 100% { opacity: 0; } }
|
|
@keyframes iso-fadeio { 0%, 100% { opacity: 0; } 30%, 70% { opacity: 1; } }
|
|
@keyframes iso-flip16 { 0% { visibility: visible; } 6.25%, 100% { visibility: hidden; } }
|
|
@keyframes iso-ram { 0%, 20%, 100% { transform: translate(0, 0); } 45%, 55% { transform: translate(var(--dx), var(--dy)); } }
|
|
@keyframes iso-move { from { transform: translate(0, 0); } to { transform: translate(var(--dx), var(--dy)); } }
|
|
@keyframes iso-shuttle {
|
|
0%, 8% { transform: translate(0, 0); }
|
|
46%, 56% { transform: translate(var(--dx), var(--dy)); }
|
|
94%, 100% { transform: translate(0, 0); }
|
|
}
|
|
@keyframes iso-drive {
|
|
0% { transform: translate(0, 0); opacity: 0; }
|
|
5% { opacity: 1; }
|
|
92% { transform: translate(var(--dx), var(--dy)); opacity: 1; }
|
|
96%, 100% { transform: translate(var(--dx), var(--dy)); opacity: 0; }
|
|
}
|
|
/* a system asking for less motion: still pictures, until the viewer turns animation on (iso-anim-on) */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.iso-wrap:not(.iso-anim-on) .iso-svg .iso-a, .iso-wrap:not(.iso-anim-on) .iso-svg .iso-ledr, .iso-wrap:not(.iso-anim-on) .iso-svg .iso-ledo { animation: none !important; }
|
|
.iso-wrap:not(.iso-anim-on) .iso-svg :is(.iso-puff, .iso-puff2, .iso-smoke, .iso-disc, .iso-glint, .iso-rip) { display: none; }
|
|
.iso-wrap:not(.iso-anim-on) .iso-svg .iso-park { display: inline; }
|
|
}
|
|
@media (max-width: 640px) {
|
|
.iso-stage { aspect-ratio: 1 / 1; min-height: 320px; }
|
|
.iso-bar { gap: 3px; top: 6px; left: 6px; right: 6px; }
|
|
.iso-bar button { padding: 4px 6px; min-width: 26px; min-height: 30px; }
|
|
.iso-compass { padding: 3px; min-height: 30px; }
|
|
.iso-compass b { display: none; }
|
|
}
|
|
@media (max-width: 370px) { .iso-compass { display: none; } }
|
|
|
|
/* full screen: native (:fullscreen) or pinned over the page where the Fullscreen API is missing (iPhone) */
|
|
.iso-fs { display: flex !important; flex-direction: column; box-sizing: border-box; overflow: hidden; }
|
|
.iso-fs-fake { position: fixed !important; inset: 0; z-index: 2147483000; margin: 0 !important; border-radius: 0 !important;
|
|
width: auto !important; height: auto !important; max-width: none !important; }
|
|
.iso-wrap.iso-fs { background: var(--iso-fsbg, #0e1117); padding: 0; }
|
|
html[data-theme=light] .iso-wrap.iso-fs { --iso-fsbg: #eef3f9; }
|
|
html.iso-fs-lock, html.iso-fs-lock body { overflow: hidden !important; }
|
|
.iso-fs .iso-fs-grow, .iso-fs .iso-wrap, .iso-wrap.iso-fs { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; margin: 0; }
|
|
.iso-fs .iso-stage { flex: 1 1 auto; aspect-ratio: auto; max-height: none; min-height: 0; height: auto; }
|
|
.iso-fs .iso-note { display: none; }
|
|
.iso-fullbtn svg { display: block; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
|
|
.iso-fullbtn .iso-fs-out, .iso-fs .iso-fullbtn .iso-fs-in { display: none; }
|
|
.iso-fs .iso-fullbtn .iso-fs-out { display: inline; }
|
|
|
|
/* fill a sized container instead of the 16:10 card (Studio) */
|
|
.iso-fill, .iso-fill > .iso-wrap { height: 100%; display: flex; flex-direction: column; }
|
|
.iso-fill .iso-stage { flex: 1 1 auto; aspect-ratio: auto; max-height: none; min-height: 0; border-radius: 0; border: 0; }
|