Spaces:
Running
Running
Download src/neon-frame.css from bep40/VSEARCH: direct link, hf CLI and curl.
- Browser
- Download file 5.9 kB
-
https://huggingface.co/spaces/bep40/VSEARCH/resolve/main/src/neon-frame.css
- Command line
-
hf download hf://spaces/bep40/VSEARCH/src/neon-frame.css
-
curl -L -o neon-frame.css https://huggingface.co/spaces/bep40/VSEARCH/resolve/main/src/neon-frame.css
5.9 kB
| /** | |
| * --neon-angle must be REGISTERED to be animatable: an unregistered custom | |
| * property is not a number the engine can interpolate, so a keyframe from 0deg | |
| * to 360deg would flip once at the midpoint instead of sweeping. | |
| * | |
| * `inherits: false` keeps the angle on the ring itself. The tile is transformed | |
| * every frame by the field engine, and an inherited animated property would be | |
| * recomputed against a moving box, which is how the gradient ends up sliding | |
| * relative to the picture it is supposed to be travelling around. | |
| * | |
| * These rules are kept byte-compatible with dist/assets/neon-frame.css, which is | |
| * what the Space loads today on top of the built app.css. The build therefore | |
| * reproduces this frame rather than replacing it, and the override file becomes | |
| * a no-op instead of a divergence between two definitions of the same effect. | |
| */ | |
| @property --neon-angle { | |
| syntax: '<angle>'; | |
| inherits: false; | |
| initial-value: 0deg; | |
| } | |
| .image-tile { --neon: #22f5d0; --neon-soft: #7ef9ff; --neon-run: 2.6s; } | |
| .image-tile.exact { --relevance-opacity: 1 ; --relevance-blur: 0px ; } | |
| /* A featured tile -- the exact answer, or a photograph just added on | |
| vai-avatar -- is never dimmed either. Same reason: it is the thing being | |
| pointed at, and a ring around something at 14% opacity is not a pointer. */ | |
| .image-tile.featured { --relevance-opacity: 1 ; --relevance-blur: 0px ; } | |
| /* The ring is drawn on `.exact` AND `.featured`. They are not alternatives: the | |
| brief asks for the running neon on the exact result in every query AND on the | |
| newest photographs, so both ring. A featured tile that is also the exact | |
| answer matches both selectors and still renders exactly one ring. */ | |
| .image-tile.exact::before, | |
| .image-tile.featured::before { | |
| content: ''; position: absolute; inset: 0; z-index: 2; | |
| border-radius: 2px; | |
| /* A 1px gap between the photo edge and the light keeps the frame legible | |
| against the tile's dark background. */ | |
| padding: 1px; | |
| /* `from var(--neon-angle)` is the whole effect. The layer itself never moves: | |
| no transform, so the mask stays welded to the tile's edges and the gradient | |
| is what sweeps. The stops are a white-hot head, a comet tail fading back | |
| round to 250deg, and a clear gap before the head returns -- so the eye | |
| reads one light travelling rather than a ring pulsing in place. */ | |
| background: conic-gradient(from var(--neon-angle, 0deg), | |
| var(--neon) 0deg, | |
| var(--neon-soft) 38deg, | |
| #ffffff 70deg, | |
| var(--neon-soft) 102deg, | |
| var(--neon) 148deg, | |
| #22f5d05c 250deg, | |
| #22f5d000 344deg, | |
| var(--neon) 360deg); | |
| -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); | |
| -webkit-mask-composite: xor; | |
| mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); | |
| mask-composite: exclude; | |
| /* Any transform here drags the mask off the frame, which is exactly the bug | |
| this replaced. Stated explicitly so a later edit cannot reintroduce it. */ | |
| transform: none; | |
| pointer-events: none; | |
| } | |
| /* The halo: the same sweep from the same angle, spread wider and blurred, so | |
| the light blooms off the edge instead of being cut flat against it. A | |
| pseudo-element can only carry one background, and a neon frame needs both the | |
| stroke and the glow -- hence two layers, one pseudo-element apart. */ | |
| .image-tile.exact::after, | |
| .image-tile.featured::after { | |
| content: ''; position: absolute; inset: 0; z-index: 1; | |
| border-radius: 2px; | |
| /* Half the previous padding, halving the ring width. */ | |
| padding: 2.5px; | |
| background: conic-gradient(from var(--neon-angle, 0deg), | |
| #22f5d000 0deg, | |
| #22f5d085 62deg, | |
| #b6fdffc4 92deg, | |
| #22f5d05e 138deg, | |
| #22f5d01f 250deg, | |
| #22f5d000 348deg, | |
| #22f5d000 360deg); | |
| -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); | |
| -webkit-mask-composite: xor; | |
| mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); | |
| mask-composite: exclude; | |
| filter: blur(4px); | |
| opacity: .82; | |
| /* No box-shadow animation. A shadow's shape is the tile's shape, so it | |
| breathes in place and can never run along the perimeter -- which is why it | |
| is replaced by the gradient sweep above rather than merely re-timed. */ | |
| box-shadow: none; | |
| pointer-events: none; | |
| } | |
| /* The head sweeps one full turn clockwise; the halo sweeps the same turn over | |
| the same duration, so the two re-align every lap instead of drifting apart. */ | |
| @keyframes neon-run { | |
| from { --neon-angle: 0deg; } | |
| to { --neon-angle: 360deg; } | |
| } | |
| /* | |
| * Gate the animation behind @supports at-rule(@property). | |
| * | |
| * Without the registration the angle is an untyped token, so `neon-run` would | |
| * jump from 0deg to 360deg at the halfway point -- the ring would blink rather | |
| * than run, which is worse than the plain static ring it replaced. Declaring the | |
| * angle and then animating it unconditionally would put that blink on every | |
| * browser that has not shipped @property. Browsers outside the gate keep the | |
| * gradient, which is a lit neon border with the bright head parked at the top. | |
| */ | |
| @supports at-rule(@property) { | |
| .image-tile.exact::before, | |
| .image-tile.featured::before, | |
| .image-tile.exact::after, | |
| .image-tile.featured::after { | |
| animation: neon-run var(--neon-run) linear infinite; | |
| } | |
| } | |
| /* A featured tile runs faster and thinner, so the newest photographs are | |
| distinguishable from an exact hit by pace as well as by size. */ | |
| .image-tile.featured { --neon-run: 2s; } | |
| .image-tile.featured::before { padding: 1.5px; } | |
| /* A slow, low-frequency sweep reads as "lit"; a fast strobe reads as an alarm, | |
| and this outline marks a correct answer rather than an error. */ | |
| @media (prefers-reduced-motion: reduce) { | |
| .image-tile.exact::before, .image-tile.exact::after, | |
| .image-tile.featured::before, .image-tile.featured::after { animation: none; } | |
| } | |