fix: viewport-fixed, deterministic background so content changes don't shift it
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
This commit is contained in:
@@ -14,20 +14,28 @@
|
||||
el.hidden = false;
|
||||
};
|
||||
|
||||
// Звёзды: 90 по небу, 170 в полосе (дуга −14°)
|
||||
// Звёзды: 90 по небу, 170 в полосе (дуга −14°). Генератор с фиксированным зерном,
|
||||
// чтобы небо было одинаковым при каждой перезагрузке и не «прыгало» после действий.
|
||||
let seed = 0x48414430; // "HAD0"
|
||||
const rand = () => {
|
||||
seed = (seed + 0x6D2B79F5) | 0;
|
||||
let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
||||
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
const star = (color, size, bright, x, y, dur, delay) => {
|
||||
const s = document.createElement('div');
|
||||
s.style.cssText = `position:absolute;border-radius:50%;left:${x}%;top:${y}%;width:${size}px;height:${size}px;background:${color};${bright ? `box-shadow:0 0 6px ${color};` : ''}animation:skTwinkle ${dur}s ease-in-out ${delay}s infinite`;
|
||||
return s;
|
||||
};
|
||||
const sky = document.getElementById('stars'), band = document.getElementById('band');
|
||||
for (let i = 0; i < 90; i++) sky.appendChild(star(Math.random() < .2 ? '#8FD6DC' : '#F2ECE0', Math.random() < .85 ? 1.5 : 2.5, false, (Math.random() * 100).toFixed(2), (Math.random() * 100).toFixed(2), (3 + Math.random() * 5).toFixed(1), (-Math.random() * 8).toFixed(1)));
|
||||
for (let i = 0; i < 90; i++) sky.appendChild(star(rand() < .2 ? '#8FD6DC' : '#F2ECE0', rand() < .85 ? 1.5 : 2.5, false, (rand() * 100).toFixed(2), (rand() * 100).toFixed(2), (3 + rand() * 5).toFixed(1), (-rand() * 8).toFixed(1)));
|
||||
for (let j = 0; j < 170; j++) {
|
||||
const bright = Math.random() < .08, r = Math.random();
|
||||
const bright = rand() < .08, r = rand();
|
||||
const color = r < .68 ? '#F2ECE0' : (r < .88 ? '#F7CEDC' : '#8FD6DC');
|
||||
const g = (Math.random() + Math.random() + Math.random()) / 3, x = Math.random() * 100;
|
||||
const g = (rand() + rand() + rand()) / 3, x = rand() * 100;
|
||||
const arc = 28 * Math.pow((x - 50) / 50, 2) + Math.max(0, 35 - x) * .35;
|
||||
band.appendChild(star(color, bright ? 2.2 : (0.8 + Math.random() * 1.1).toFixed(1), bright, x.toFixed(2), (6 + g * 55 + arc).toFixed(2), (2.5 + Math.random() * 5).toFixed(1), (-Math.random() * 8).toFixed(1)));
|
||||
band.appendChild(star(color, bright ? 2.2 : (0.8 + rand() * 1.1).toFixed(1), bright, x.toFixed(2), (6 + g * 55 + arc).toFixed(2), (2.5 + rand() * 5).toFixed(1), (-rand() * 8).toFixed(1)));
|
||||
}
|
||||
|
||||
// Панели архив/настройки
|
||||
|
||||
@@ -15,11 +15,11 @@ body{min-height:100vh;color:var(--washi);font-family:'Space Grotesk',sans-serif;
|
||||
a{color:var(--byakugun);text-decoration-color:rgba(143,214,220,.4)}a:hover{color:var(--washi)}
|
||||
input,select,button{font:inherit}button[disabled]{opacity:.45;pointer-events:none}
|
||||
.mono{font-family:'Space Mono',monospace}.serif{font-family:'Shippori Mincho',serif}
|
||||
.layer{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0}
|
||||
.nebula{position:absolute;pointer-events:none;z-index:0;top:26%;left:-22%;width:150%;height:52%;filter:blur(34px);opacity:.72;animation:skNebula 46s ease-in-out infinite;background:radial-gradient(ellipse 42% 58% at 20% 60%,rgba(234,143,174,.20),transparent 70%),radial-gradient(ellipse 58% 46% at 48% 44%,rgba(232,78,138,.14),transparent 72%),radial-gradient(ellipse 38% 54% at 70% 57%,rgba(159,143,203,.17),transparent 70%),radial-gradient(ellipse 28% 40% at 88% 51%,rgba(143,214,220,.11),transparent 70%),radial-gradient(ellipse 52% 34% at 36% 62%,rgba(53,92,214,.11),transparent 72%)}
|
||||
.core{position:absolute;pointer-events:none;z-index:0;top:44%;left:-15%;width:135%;height:14%;filter:blur(14px);opacity:.7;animation:skNebulaCore 58s ease-in-out infinite;background:radial-gradient(ellipse 20% 70% at 8% 71%,rgba(247,206,220,.22),transparent 70%),radial-gradient(ellipse 14% 90% at 22% 52%,rgba(242,236,224,.18),transparent 70%),radial-gradient(ellipse 18% 80% at 36% 48%,rgba(234,143,174,.22),transparent 72%),radial-gradient(ellipse 12% 100% at 48% 38%,rgba(247,206,220,.24),transparent 70%),radial-gradient(ellipse 16% 85% at 60% 43%,rgba(242,236,224,.17),transparent 72%),radial-gradient(ellipse 13% 75% at 72% 43%,rgba(234,143,174,.20),transparent 70%),radial-gradient(ellipse 18% 90% at 86% 55%,rgba(247,206,220,.18),transparent 72%),radial-gradient(ellipse 10% 60% at 94% 61%,rgba(143,214,220,.12),transparent 70%)}
|
||||
.dust{position:absolute;pointer-events:none;z-index:0;top:44%;left:-15%;width:135%;height:14%;filter:blur(10px);opacity:.8;animation:skNebula 46s ease-in-out infinite;background:radial-gradient(ellipse 20% 38% at 16% 72%,rgba(7,9,18,.6),transparent 75%),radial-gradient(ellipse 28% 34% at 44% 55%,rgba(9,11,22,.55),transparent 75%),radial-gradient(ellipse 18% 38% at 70% 50%,rgba(7,9,18,.55),transparent 75%),radial-gradient(ellipse 14% 30% at 90% 68%,rgba(9,11,22,.5),transparent 75%)}
|
||||
.band{position:absolute;pointer-events:none;z-index:0;top:44%;left:-15%;width:135%;height:14%;transform:rotate(-14deg)}
|
||||
.layer{position:fixed;inset:0;overflow:hidden;pointer-events:none;z-index:0}
|
||||
.nebula{position:fixed;pointer-events:none;z-index:0;top:26%;left:-22%;width:150%;height:52%;filter:blur(34px);opacity:.72;animation:skNebula 46s ease-in-out infinite;background:radial-gradient(ellipse 42% 58% at 20% 60%,rgba(234,143,174,.20),transparent 70%),radial-gradient(ellipse 58% 46% at 48% 44%,rgba(232,78,138,.14),transparent 72%),radial-gradient(ellipse 38% 54% at 70% 57%,rgba(159,143,203,.17),transparent 70%),radial-gradient(ellipse 28% 40% at 88% 51%,rgba(143,214,220,.11),transparent 70%),radial-gradient(ellipse 52% 34% at 36% 62%,rgba(53,92,214,.11),transparent 72%)}
|
||||
.core{position:fixed;pointer-events:none;z-index:0;top:44%;left:-15%;width:135%;height:14%;filter:blur(14px);opacity:.7;animation:skNebulaCore 58s ease-in-out infinite;background:radial-gradient(ellipse 20% 70% at 8% 71%,rgba(247,206,220,.22),transparent 70%),radial-gradient(ellipse 14% 90% at 22% 52%,rgba(242,236,224,.18),transparent 70%),radial-gradient(ellipse 18% 80% at 36% 48%,rgba(234,143,174,.22),transparent 72%),radial-gradient(ellipse 12% 100% at 48% 38%,rgba(247,206,220,.24),transparent 70%),radial-gradient(ellipse 16% 85% at 60% 43%,rgba(242,236,224,.17),transparent 72%),radial-gradient(ellipse 13% 75% at 72% 43%,rgba(234,143,174,.20),transparent 70%),radial-gradient(ellipse 18% 90% at 86% 55%,rgba(247,206,220,.18),transparent 72%),radial-gradient(ellipse 10% 60% at 94% 61%,rgba(143,214,220,.12),transparent 70%)}
|
||||
.dust{position:fixed;pointer-events:none;z-index:0;top:44%;left:-15%;width:135%;height:14%;filter:blur(10px);opacity:.8;animation:skNebula 46s ease-in-out infinite;background:radial-gradient(ellipse 20% 38% at 16% 72%,rgba(7,9,18,.6),transparent 75%),radial-gradient(ellipse 28% 34% at 44% 55%,rgba(9,11,22,.55),transparent 75%),radial-gradient(ellipse 18% 38% at 70% 50%,rgba(7,9,18,.55),transparent 75%),radial-gradient(ellipse 14% 30% at 90% 68%,rgba(9,11,22,.5),transparent 75%)}
|
||||
.band{position:fixed;pointer-events:none;z-index:0;top:44%;left:-15%;width:135%;height:14%;transform:rotate(-14deg)}
|
||||
.sister{position:absolute}.sister .col{display:flex;flex-direction:column;align-items:center;gap:9px}
|
||||
.skdot{pointer-events:auto;border-radius:50%}.sklbl{font-family:'Space Mono',monospace;font-size:9px;letter-spacing:.3em;color:var(--nibi);white-space:nowrap;opacity:0;transition:opacity .4s}.skdot:hover+.sklbl{opacity:1}
|
||||
header{position:relative;z-index:1;display:flex;justify-content:space-between;align-items:flex-start;padding:30px 44px 0}
|
||||
|
||||
@@ -96,6 +96,20 @@ class InboxPageTest extends TestCase
|
||||
$this->assertStringNotContainsString('class="counter"', $html);
|
||||
}
|
||||
|
||||
public function test_background_is_viewport_fixed_and_deterministic(): void
|
||||
{
|
||||
// Фон не должен пересчитываться при росте документа (раскрытие панелей)
|
||||
// и перемешиваться при перезагрузке после действий.
|
||||
$html = $this->get('/', ['X-Remote-User' => 'nikita'])->assertOk()->getContent();
|
||||
foreach (['.layer{position:fixed', '.nebula{position:fixed', '.core{position:fixed', '.dust{position:fixed', '.band{position:fixed'] as $rule) {
|
||||
$this->assertStringContainsString($rule, $html);
|
||||
}
|
||||
|
||||
$js = file_get_contents(public_path('inbox.js'));
|
||||
$this->assertStringNotContainsString('Math.random', $js);
|
||||
$this->assertStringContainsString('const rand = ', $js);
|
||||
}
|
||||
|
||||
public function test_hidden_attribute_beats_flex_display_rules(): void
|
||||
{
|
||||
// .panel .body{display:flex} иначе перебивает браузерное [hidden]{display:none},
|
||||
|
||||
Reference in New Issue
Block a user