fix: apply review findings; make ops strip text CSS-generated
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, onUnmounted, shallowRef } from 'vue';
|
import { onMounted, onUnmounted, shallowRef, watch } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{ allowMotion: boolean }>();
|
const props = defineProps<{ allowMotion: boolean }>();
|
||||||
|
|
||||||
@@ -26,10 +26,14 @@ const finish = (): void => {
|
|||||||
|
|
||||||
const onKey = (): void => finish();
|
const onKey = (): void => finish();
|
||||||
|
|
||||||
onMounted(() => {
|
let started = false;
|
||||||
|
|
||||||
|
const start = (): void => {
|
||||||
|
if (started || !props.allowMotion || navigator.webdriver) return;
|
||||||
|
started = true;
|
||||||
let seen = false;
|
let seen = false;
|
||||||
try { seen = window.sessionStorage.getItem(SEEN_KEY) === '1'; } catch { seen = false; }
|
try { seen = window.sessionStorage.getItem(SEEN_KEY) === '1'; } catch { seen = false; }
|
||||||
if (!props.allowMotion || seen || navigator.webdriver) return;
|
if (seen) return;
|
||||||
try { window.sessionStorage.setItem(SEEN_KEY, '1'); } catch { /* optional */ }
|
try { window.sessionStorage.setItem(SEEN_KEY, '1'); } catch { /* optional */ }
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
LINES.forEach((_, index) => {
|
LINES.forEach((_, index) => {
|
||||||
@@ -37,6 +41,11 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
timers.push(window.setTimeout(finish, 260 + LINES.length * 280 + 500));
|
timers.push(window.setTimeout(finish, 260 + LINES.length * 280 + 500));
|
||||||
window.addEventListener('keydown', onKey, { once: true });
|
window.addEventListener('keydown', onKey, { once: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
// The motion policy resolves in the parent's onMounted (after ours), so react to the prop.
|
||||||
|
onMounted(() => {
|
||||||
|
watch(() => props.allowMotion, start, { immediate: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { bucharestClock, uptimeSince } from '@/lib/operations';
|
|||||||
/** Start of the current hosting-admin role (content file: Trupa lui Răzuș, Aug 2025). */
|
/** Start of the current hosting-admin role (content file: Trupa lui Răzuș, Aug 2025). */
|
||||||
const SHIFT_START = new Date('2025-08-01T00:00:00Z');
|
const SHIFT_START = new Date('2025-08-01T00:00:00Z');
|
||||||
|
|
||||||
const clock = shallowRef<string | null>(null);
|
const clock = shallowRef('--:--');
|
||||||
const shift = shallowRef<string | null>(null);
|
const shift = shallowRef('-- mo');
|
||||||
let timer = 0;
|
let timer = 0;
|
||||||
|
|
||||||
const tick = (): void => {
|
const tick = (): void => {
|
||||||
@@ -24,10 +24,11 @@ onUnmounted(() => window.clearInterval(timer));
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- Purely decorative telemetry: all text is CSS-generated so the semantic document is unchanged. -->
|
||||||
<aside class="ops-strip" aria-hidden="true">
|
<aside class="ops-strip" aria-hidden="true">
|
||||||
<span class="ops-strip__dot" />
|
<span class="ops-strip__dot" />
|
||||||
<span>Bucharest <strong>{{ clock ?? '--:--' }}</strong></span>
|
<span class="ops-strip__item" data-label="Bucharest" :data-value="clock" />
|
||||||
<span class="ops-strip__sep">/</span>
|
<span class="ops-strip__sep" />
|
||||||
<span>On shift <strong>{{ shift ?? '-- mo' }}</strong></span>
|
<span class="ops-strip__item" data-label="On shift" :data-value="shift" />
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { onMounted, onUnmounted, type Ref } from 'vue';
|
import { onMounted, onUnmounted, watch, type Ref } from 'vue';
|
||||||
|
|
||||||
const SURFACES = '.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure';
|
const SURFACES = '.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure';
|
||||||
|
|
||||||
@@ -37,6 +37,12 @@ export function useHeadlight(enabled: Ref<boolean>): void {
|
|||||||
if (frame === 0) frame = requestAnimationFrame(flush);
|
if (frame === 0) frame = requestAnimationFrame(flush);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(enabled, (value) => {
|
||||||
|
if (value) return;
|
||||||
|
active = false;
|
||||||
|
delete document.documentElement.dataset.headlight;
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!window.matchMedia('(pointer: fine)').matches) return;
|
if (!window.matchMedia('(pointer: fine)').matches) return;
|
||||||
window.addEventListener('pointermove', onMove, { passive: true });
|
window.addEventListener('pointermove', onMove, { passive: true });
|
||||||
|
|||||||
@@ -41,8 +41,10 @@ body::after {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.ops-strip strong { color: var(--text); font-weight: 600; }
|
.ops-strip__item::before { content: attr(data-label) ' '; }
|
||||||
|
.ops-strip__item::after { content: attr(data-value); color: var(--text); font-weight: 600; }
|
||||||
.ops-strip__sep { opacity: 0.4; }
|
.ops-strip__sep { opacity: 0.4; }
|
||||||
|
.ops-strip__sep::before { content: '/'; }
|
||||||
.ops-strip__dot {
|
.ops-strip__dot {
|
||||||
width: 7px;
|
width: 7px;
|
||||||
height: 7px;
|
height: 7px;
|
||||||
@@ -90,7 +92,7 @@ html[data-headlight='on'] .reading-viewport::before {
|
|||||||
content: '';
|
content: '';
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: var(--z-atmosphere);
|
z-index: -1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background: radial-gradient(420px circle at var(--hx, 50%) var(--hy, 50%), rgb(255 214 147 / 10%), transparent 70%);
|
background: radial-gradient(420px circle at var(--hx, 50%) var(--hy, 50%), rgb(255 214 147 / 10%), transparent 70%);
|
||||||
mix-blend-mode: screen;
|
mix-blend-mode: screen;
|
||||||
@@ -102,11 +104,13 @@ html[data-headlight='on'] .reading-viewport::before {
|
|||||||
.contact-destination,
|
.contact-destination,
|
||||||
.infrastructure-flow-figure {
|
.infrastructure-flow-figure {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
html[data-headlight='on'] :is(.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure)::after {
|
html[data-headlight='on'] :is(.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure)::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 50%), rgb(120 216 229 / 14%), transparent 65%);
|
background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 50%), rgb(120 216 229 / 14%), transparent 65%);
|
||||||
@@ -124,7 +128,7 @@ html[data-headlight='on'] :is(.project-case-study, .project-card, .contact-desti
|
|||||||
.portfolio-section { counter-increment: chapter; }
|
.portfolio-section { counter-increment: chapter; }
|
||||||
.portfolio-section > h2::before,
|
.portfolio-section > h2::before,
|
||||||
.project-stage__lead > h2::before {
|
.project-stage__lead > h2::before {
|
||||||
content: '0' counter(chapter);
|
content: '0' counter(chapter) / '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset-block-start: 50%;
|
inset-block-start: 50%;
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
@@ -142,7 +146,10 @@ html[data-headlight='on'] :is(.project-case-study, .project-card, .contact-desti
|
|||||||
}
|
}
|
||||||
@media (forced-colors: active) {
|
@media (forced-colors: active) {
|
||||||
.portfolio-section > h2::before,
|
.portfolio-section > h2::before,
|
||||||
.project-stage__lead > h2::before { display: none; }
|
.project-stage__lead > h2::before,
|
||||||
|
body::after,
|
||||||
|
.reading-viewport::before,
|
||||||
|
:is(.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure)::after { display: none !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Kinetic type + scroll-linked reveals (progressive enhancement). */
|
/* Kinetic type + scroll-linked reveals (progressive enhancement). */
|
||||||
@@ -156,7 +163,7 @@ html[data-headlight='on'] :is(.project-case-study, .project-card, .contact-desti
|
|||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
}
|
}
|
||||||
@supports (animation-timeline: view()) {
|
@supports (animation-timeline: view()) {
|
||||||
.project-card {
|
.reading-stage:not([data-reading-stage='live']) .project-card {
|
||||||
animation: ns-rise linear both;
|
animation: ns-rise linear both;
|
||||||
animation-timeline: view();
|
animation-timeline: view();
|
||||||
animation-range: entry 0% entry 40%;
|
animation-range: entry 0% entry 40%;
|
||||||
@@ -170,5 +177,8 @@ html[data-headlight='on'] :is(.project-case-study, .project-card, .contact-desti
|
|||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
body::after, .ops-strip, .boot-sequence { display: none !important; }
|
body::after, .ops-strip, .boot-sequence { display: none !important; }
|
||||||
.portfolio-section > h2::before { display: none; }
|
.portfolio-section > h2::before,
|
||||||
|
.project-stage__lead > h2::before,
|
||||||
|
.reading-viewport::before,
|
||||||
|
:is(.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure)::after { display: none !important; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user