Rebuild the public website in the shared design system
Reported plainly: the site is still in the old style. It was — it was the last of the three surfaces the token file describes that had never been converted. landing.blade.php carried its own complete design system inside a <style> block: warm paper, IBM Plex Serif headlines, 3px corners, its own scale of greys, written before the tokens existed. Somebody who clicked "Anmelden" left one company and arrived at another. The page now draws from resources/css/portal-tokens.css like the portal and the console: same surfaces, same radii, same button hierarchy, same small-caps label, one family at weight 700 instead of a serif. What still differs is rhythm, which is what a marketing page is allowed to differ in. Content is unchanged. New: components/layouts/site.blade.php holds the header and footer for every public page, and legal.blade.php uses it too — the imprint used to be a stranded card in a third style again. Two things found on the way: - .lbl set `color: var(--muted)`, which is not a token. An undefined var() in `color` computes to `unset`, so every small-caps label on every surface was whatever colour its container happened to be. - The big figures were set in mono. Mono gives the decimal comma a full glyph cell, so "99,95" read as three separate numbers. coming-soon and the status page stay self-contained on purpose — both are served when the application may not have a built stylesheet at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>feat/granted-plans v1.3.4
parent
f8f0de4892
commit
43d58de7f8
|
|
@ -82,6 +82,43 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── The public website ───────────────────────────────────────────────────
|
||||||
|
* The marketing page draws from the same tokens as the portal and the console
|
||||||
|
* and differs only in rhythm. These four rules are what Tailwind cannot say:
|
||||||
|
* a state class toggled by an observer, and the disclosure marker.
|
||||||
|
*/
|
||||||
|
.rv {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(16px);
|
||||||
|
transition: opacity 0.55s var(--ease), transform 0.55s var(--ease);
|
||||||
|
}
|
||||||
|
.rv.in { opacity: 1; transform: none; }
|
||||||
|
/* Staggered siblings — the delay belongs to the element, not to the observer,
|
||||||
|
so a card that scrolls in alone still arrives on its own schedule. Kept
|
||||||
|
short: a row of three cards that takes a second to finish assembling reads
|
||||||
|
as a slow page, not as a considered one. */
|
||||||
|
.rv.d1 { transition-delay: 0.07s; }
|
||||||
|
.rv.d2 { transition-delay: 0.14s; }
|
||||||
|
|
||||||
|
/* The section rule draws across as its heading arrives. Slower than the
|
||||||
|
heading it belongs to, so it reads as a consequence rather than as a second
|
||||||
|
thing happening at the same time. */
|
||||||
|
.rv .fill { transform: scaleX(0); transform-origin: left; transition: transform 1s var(--ease) 0.1s; }
|
||||||
|
.rv.in .fill { transform: scaleX(1); }
|
||||||
|
|
||||||
|
.logline { opacity: 0; transform: translateX(-10px); transition: opacity 0.45s var(--ease), transform 0.45s var(--ease); }
|
||||||
|
.logline.in { opacity: 1; transform: none; }
|
||||||
|
|
||||||
|
/* The FAQ disclosure. Safari needs the -webkit- selector as well, and without
|
||||||
|
`list-style: none` Firefox keeps drawing its own triangle beside ours. */
|
||||||
|
.faq summary { list-style: none; cursor: pointer; }
|
||||||
|
.faq summary::-webkit-details-marker { display: none; }
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.rv, .logline { opacity: 1 !important; transform: none !important; }
|
||||||
|
.rv .fill { transform: scaleX(1) !important; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Metric visuals ───────────────────────────────────────────────────────
|
/* ── Metric visuals ───────────────────────────────────────────────────────
|
||||||
* The ring and the sparkline from the approved template. They live here
|
* The ring and the sparkline from the approved template. They live here
|
||||||
* rather than as utilities because both need keyframes and a per-element
|
* rather than as utilities because both need keyframes and a per-element
|
||||||
|
|
|
||||||
|
|
@ -115,5 +115,10 @@
|
||||||
font-size: 11.5px;
|
font-size: 11.5px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.07em;
|
letter-spacing: 0.07em;
|
||||||
color: var(--muted);
|
/* --text-muted, not --muted: the latter is not a token and never was. An
|
||||||
|
undefined var() in `color` does not make the browser skip the
|
||||||
|
declaration, it computes to `unset` — which for an inherited property
|
||||||
|
means "take the parent's colour". Every label on every surface was
|
||||||
|
therefore whatever its container happened to be. */
|
||||||
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
@props([
|
||||||
|
'title' => null,
|
||||||
|
'description' => null,
|
||||||
|
'robots' => null,
|
||||||
|
])
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" class="scroll-smooth">
|
||||||
|
<head>
|
||||||
|
@if ($description)<meta name="description" content="{{ $description }}">@endif
|
||||||
|
@if ($robots)<meta name="robots" content="{{ $robots }}">@endif
|
||||||
|
<x-shell.head :title="$title" />
|
||||||
|
{{-- Everything below the fold arrives on scroll, which an observer decides.
|
||||||
|
Without JavaScript nothing is ever observed, so the whole page would be
|
||||||
|
a headline over a blank sheet. --}}
|
||||||
|
<noscript><style>.rv,.logline{opacity:1!important;transform:none!important}.rv .fill{transform:scaleX(1)!important}</style></noscript>
|
||||||
|
</head>
|
||||||
|
{{--
|
||||||
|
The public website.
|
||||||
|
|
||||||
|
It used to be a self-contained page with its own design system inside a
|
||||||
|
<style> block: warm paper, a serif headline, 3px corners, its own scale of
|
||||||
|
greys. It predated the token file, and by the time the portal and the
|
||||||
|
console had been rebuilt it was the only surface left speaking the old
|
||||||
|
language — a visitor moved from the website to the sign-in form and arrived
|
||||||
|
somewhere that looked like a different company.
|
||||||
|
|
||||||
|
So it draws from resources/css/portal-tokens.css like everything else. Same
|
||||||
|
colours, same radii, same buttons, same small-caps label. What still differs
|
||||||
|
is density and rhythm, which is what a marketing page is allowed to differ
|
||||||
|
in: wider measure, larger type, more air between sections.
|
||||||
|
--}}
|
||||||
|
{{-- Column, so the footer sits at the bottom of the viewport on a short page
|
||||||
|
rather than halfway up it with the ground colour showing underneath. --}}
|
||||||
|
<body class="flex min-h-screen flex-col bg-bg text-body antialiased">
|
||||||
|
<div id="site-progress" class="fixed inset-x-0 top-0 z-[120] h-0.5 w-0 bg-accent" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<header
|
||||||
|
x-data="{ open: false, stuck: false }"
|
||||||
|
@scroll.window="stuck = window.scrollY > 8"
|
||||||
|
{{-- Opaque in both states. A translucent bar over a page of cards and
|
||||||
|
tables lets whatever is underneath show through the wordmark, and
|
||||||
|
backdrop-blur does not hide dark text on a light ground — it only
|
||||||
|
makes it look like a rendering fault. --}}
|
||||||
|
class="fixed inset-x-0 top-0 z-[110] border-b transition-colors duration-200"
|
||||||
|
:class="stuck ? 'border-line bg-surface shadow-xs' : 'border-transparent bg-bg'"
|
||||||
|
>
|
||||||
|
<div class="mx-auto flex h-16 max-w-[1180px] items-center gap-8 px-5 sm:px-8">
|
||||||
|
<a href="{{ route('home') }}" class="flex items-center gap-2.5">
|
||||||
|
<x-ui.logo class="size-8" />
|
||||||
|
<span class="text-md font-bold tracking-[-0.02em] text-ink">CluPilot</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<nav class="ml-auto hidden items-center gap-7 text-sm text-muted lg:flex">
|
||||||
|
@foreach ([
|
||||||
|
'#produkt' => 'Produkt',
|
||||||
|
'#ablauf' => 'Ablauf',
|
||||||
|
'#sicherheit' => 'Sicherheit',
|
||||||
|
'#pakete' => 'Pakete',
|
||||||
|
'#faq' => 'Fragen',
|
||||||
|
] as $href => $label)
|
||||||
|
<a href="{{ route('home') }}{{ $href }}" class="relative py-1 transition-colors hover:text-ink after:absolute after:inset-x-0 after:-bottom-px after:h-px after:origin-left after:scale-x-0 after:bg-accent after:transition-transform after:duration-300 hover:after:scale-x-100">{{ $label }}</a>
|
||||||
|
@endforeach
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="ml-auto flex items-center gap-3 lg:ml-0">
|
||||||
|
<a href="{{ route('login') }}" class="hidden text-sm text-muted transition-colors hover:text-ink sm:block">Anmelden</a>
|
||||||
|
<x-ui.button href="{{ route('home') }}#pakete" variant="ink" size="sm">Pakete ansehen</x-ui.button>
|
||||||
|
|
||||||
|
<button type="button" class="-mr-1 grid size-9 place-items-center rounded text-muted transition-colors hover:bg-surface-hover hover:text-ink lg:hidden" @click="open = !open" :aria-expanded="open" aria-label="Menü">
|
||||||
|
<x-ui.icon name="menu" class="size-5" x-show="!open" />
|
||||||
|
<x-ui.icon name="x" class="size-5" x-show="open" x-cloak />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav x-show="open" x-cloak @click="open = false" class="border-t border-line bg-surface px-5 py-2 sm:px-8 lg:hidden">
|
||||||
|
@foreach ([
|
||||||
|
'#produkt' => 'Produkt',
|
||||||
|
'#ablauf' => 'Ablauf',
|
||||||
|
'#sicherheit' => 'Sicherheit',
|
||||||
|
'#pakete' => 'Pakete',
|
||||||
|
'#faq' => 'Fragen',
|
||||||
|
route('login') => 'Anmelden',
|
||||||
|
] as $href => $label)
|
||||||
|
<a href="{{ Str::startsWith($href, '#') ? route('home').$href : $href }}" class="flex min-h-11 items-center border-b border-line text-sm text-body last:border-0">{{ $label }}</a>
|
||||||
|
@endforeach
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="flex-1">
|
||||||
|
{{ $slot }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="border-t border-line bg-surface">
|
||||||
|
<div class="mx-auto flex max-w-[1180px] flex-col gap-5 px-5 py-9 text-sm text-muted sm:px-8 lg:flex-row lg:items-center">
|
||||||
|
<div class="flex items-center gap-2.5">
|
||||||
|
<x-ui.logo class="size-6" />
|
||||||
|
<span class="font-semibold tracking-[-0.02em] text-ink">CluPilot Cloud</span>
|
||||||
|
</div>
|
||||||
|
<span class="lbl">© {{ date('Y') }} · Österreich</span>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap gap-x-6 gap-y-2 lg:ml-auto">
|
||||||
|
<a href="{{ route('legal.impressum') }}" class="transition-colors hover:text-ink">Impressum</a>
|
||||||
|
<a href="{{ route('legal.datenschutz') }}" class="transition-colors hover:text-ink">Datenschutz</a>
|
||||||
|
<a href="{{ route('legal.agb') }}" class="transition-colors hover:text-ink">AGB</a>
|
||||||
|
<a href="{{ route('status') }}" class="transition-colors hover:text-ink">Status</a>
|
||||||
|
<a href="{{ route('login') }}" class="transition-colors hover:text-ink">Anmelden</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
@verbatim
|
||||||
|
<script>
|
||||||
|
// Three small behaviours, none of which Alpine expresses more clearly
|
||||||
|
// than an IntersectionObserver does: a scroll indicator, sections that
|
||||||
|
// arrive as they are reached, and figures that count up once.
|
||||||
|
(function () {
|
||||||
|
const reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
|
||||||
|
const progress = document.getElementById('site-progress');
|
||||||
|
|
||||||
|
addEventListener('scroll', () => {
|
||||||
|
const el = document.documentElement;
|
||||||
|
const max = el.scrollHeight - el.clientHeight;
|
||||||
|
progress.style.width = (max > 0 ? (scrollY / max) * 100 : 0) + '%';
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
const reveal = new IntersectionObserver(entries => entries.forEach(entry => {
|
||||||
|
if (!entry.isIntersecting) return;
|
||||||
|
entry.target.classList.add('in');
|
||||||
|
reveal.unobserve(entry.target);
|
||||||
|
}), { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
|
||||||
|
document.querySelectorAll('.rv').forEach(el => reveal.observe(el));
|
||||||
|
|
||||||
|
// Tabular figures are set on the element, so counting never shifts
|
||||||
|
// the layout around it.
|
||||||
|
const count = new IntersectionObserver(entries => entries.forEach(entry => {
|
||||||
|
if (!entry.isIntersecting) return;
|
||||||
|
count.unobserve(entry.target);
|
||||||
|
const el = entry.target;
|
||||||
|
const target = parseFloat(el.dataset.count);
|
||||||
|
const decimals = parseInt(el.dataset.decimals || 0, 10);
|
||||||
|
const format = v => v.toFixed(decimals).replace('.', ',');
|
||||||
|
if (reduce) { el.textContent = format(target); return; }
|
||||||
|
const start = performance.now();
|
||||||
|
(function tick(now) {
|
||||||
|
const p = Math.min((now - start) / 1200, 1);
|
||||||
|
el.textContent = format(target * (1 - Math.pow(1 - p, 3)));
|
||||||
|
if (p < 1) requestAnimationFrame(tick);
|
||||||
|
})(start);
|
||||||
|
}), { threshold: 0.6 });
|
||||||
|
document.querySelectorAll('[data-count]').forEach(el => count.observe(el));
|
||||||
|
|
||||||
|
// The provisioning log writes itself out, line by line.
|
||||||
|
const log = document.getElementById('deploy-log');
|
||||||
|
if (log) {
|
||||||
|
const lines = new IntersectionObserver(entries => entries.forEach(entry => {
|
||||||
|
if (!entry.isIntersecting) return;
|
||||||
|
lines.unobserve(log);
|
||||||
|
const rows = log.querySelectorAll('.logline');
|
||||||
|
rows.forEach((row, i) => setTimeout(() => row.classList.add('in'), reduce ? 0 : 240 + i * 400));
|
||||||
|
}), { threshold: 0.35 });
|
||||||
|
lines.observe(log);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@endverbatim
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
@props([
|
||||||
|
'number',
|
||||||
|
'label',
|
||||||
|
// The security band sits on --plate, where the muted token has no contrast.
|
||||||
|
'dark' => false,
|
||||||
|
])
|
||||||
|
{{--
|
||||||
|
The rubric above every section of the public site: a number, a name, and a
|
||||||
|
rule that draws itself across as the section is reached. Written once
|
||||||
|
because eight of them by hand is how the numbering and the spacing end up
|
||||||
|
disagreeing with each other.
|
||||||
|
--}}
|
||||||
|
<div {{ $attributes->merge(['class' => 'rv mb-7 flex items-center gap-4']) }}>
|
||||||
|
<span class="font-mono text-xs font-medium {{ $dark ? 'text-accent' : 'text-accent-text' }}">§ {{ $number }}</span>
|
||||||
|
<span class="lbl {{ $dark ? '!text-plate-muted' : '' }}">{{ $label }}</span>
|
||||||
|
<span class="fill h-px flex-1 {{ $dark ? 'bg-plate-rule' : 'bg-line' }}"></span>
|
||||||
|
</div>
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,32 +1,30 @@
|
||||||
<!DOCTYPE html>
|
{{--
|
||||||
<html lang="de">
|
Impressum, Datenschutz, AGB.
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
Placeholders still — the texts are the company's to write, not mine to
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
invent. What changed is that they are now pages OF the website rather than
|
||||||
<title>{{ $title }} — CluPilot Cloud</title>
|
a standalone card in a different design: same header, same footer, so
|
||||||
@verbatim
|
somebody following the imprint link from the footer does not land on what
|
||||||
<style>
|
looks like a stranded error page.
|
||||||
:root{--accent:#f97316;--ink:#1d1d1f;--muted:#86868b;--bg:#f5f5f7;--font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
|
--}}
|
||||||
*{margin:0;box-sizing:border-box}
|
<x-layouts.site :title="$title.' — CluPilot Cloud'" robots="noindex">
|
||||||
body{font-family:var(--font);background:var(--bg);color:var(--ink);min-height:100vh;display:grid;place-items:center;padding:24px}
|
|
||||||
main{max-width:600px;text-align:center}
|
<article class="mx-auto max-w-[820px] px-5 pb-20 pt-32 sm:px-8 lg:pb-28 lg:pt-40">
|
||||||
a{color:var(--accent);text-decoration:none}
|
<p class="lbl">Rechtliches</p>
|
||||||
a:hover{text-decoration:underline}
|
<h1 class="mt-4 text-[clamp(2rem,4vw,3rem)] font-bold leading-[1.08] tracking-[-0.035em] text-ink">{{ $title }}</h1>
|
||||||
.k{color:#f97316;font-weight:700;text-transform:uppercase;letter-spacing:.1em;font-size:.8rem;margin-bottom:8px}
|
|
||||||
h1{font-size:2rem;letter-spacing:-.02em;margin-bottom:12px}
|
<div class="mt-10 rounded-xl border border-line bg-surface p-8 shadow-xs lg:p-10">
|
||||||
p{color:var(--muted);line-height:1.6;margin-bottom:8px}
|
<p class="text-md leading-relaxed text-body">Dieser Inhalt wird noch ergänzt.</p>
|
||||||
.back{display:inline-block;margin-top:24px;background:var(--accent);color:#fff;padding:10px 22px;border-radius:99px;font-weight:600}
|
<p class="mt-4 text-md leading-relaxed text-body">
|
||||||
.back:hover{text-decoration:none;background:#ea6a0a}
|
Bei Fragen erreichen Sie uns unter
|
||||||
</style>
|
<a href="mailto:office@clupilot.com" class="font-medium text-accent-text underline-offset-4 hover:underline">office@clupilot.com</a>.
|
||||||
@endverbatim
|
</p>
|
||||||
</head>
|
|
||||||
<body>
|
<x-ui.button href="{{ route('home') }}" variant="secondary" class="mt-8">
|
||||||
<main>
|
<x-ui.icon name="arrow-left" class="size-4" />
|
||||||
<p class="k">CluPilot Cloud</p>
|
Zurück zur Startseite
|
||||||
<h1>{{ $title }}</h1>
|
</x-ui.button>
|
||||||
<p>Dieser Inhalt wird noch ergänzt.</p>
|
</div>
|
||||||
<p>Bei Fragen erreichen Sie uns unter <a href="mailto:office@clupilot.com">office@clupilot.com</a>.</p>
|
</article>
|
||||||
<a class="back" href="{{ route('home') }}">Zurück zur Startseite</a>
|
|
||||||
</main>
|
</x-layouts.site>
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The public website is one of the three surfaces the token file describes.
|
||||||
|
*
|
||||||
|
* It was the last one still speaking the old language: a 900-line page with its
|
||||||
|
* own design system inside a <style> block — warm paper, a serif headline, 3px
|
||||||
|
* corners, its own scale of greys — written before the tokens existed. A
|
||||||
|
* visitor who clicked "Anmelden" left one company and arrived at another.
|
||||||
|
*
|
||||||
|
* These tests are about drift, not about looks. Nothing stops someone pasting a
|
||||||
|
* hex value into a marketing page at four in the afternoon; what stops it
|
||||||
|
* becoming a second design system is noticing on the first one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Public pages that must draw from the shared stylesheet. */
|
||||||
|
function sitePages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'landing.blade.php',
|
||||||
|
'legal.blade.php',
|
||||||
|
'components/layouts/site.blade.php',
|
||||||
|
'components/site/section-head.blade.php',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
it('builds the public site from the shared tokens, not from its own palette', function (string $page) {
|
||||||
|
$source = File::get(resource_path('views/'.$page));
|
||||||
|
|
||||||
|
// Hex literals are the tell. One is a paste; a dozen is a second design
|
||||||
|
// system growing quietly beside the first.
|
||||||
|
preg_match_all('/#[0-9a-fA-F]{3,8}\b/', $source, $found);
|
||||||
|
|
||||||
|
expect($found[0])->toBeEmpty()
|
||||||
|
// The old page set its own type stack. The token file owns that now,
|
||||||
|
// and a serif headline is precisely the impression the redesign
|
||||||
|
// removed — every page read as a document.
|
||||||
|
->and($source)->not->toContain('font-family')
|
||||||
|
->and($source)->not->toContain('Georgia')
|
||||||
|
->and($source)->not->toContain('font-serif');
|
||||||
|
})->with(sitePages());
|
||||||
|
|
||||||
|
it('serves the website from the same stylesheet as the portal', function () {
|
||||||
|
// Not "a stylesheet exists" — the SAME one. Two builds would drift the
|
||||||
|
// moment a token changed in one of them.
|
||||||
|
$layout = File::get(resource_path('views/components/layouts/site.blade.php'));
|
||||||
|
|
||||||
|
expect($layout)->toContain('<x-shell.head');
|
||||||
|
|
||||||
|
$head = File::get(resource_path('views/components/shell/head.blade.php'));
|
||||||
|
|
||||||
|
expect($head)->toContain("@vite(['resources/css/app.css'");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the two deliberately self-contained pages self-contained', function () {
|
||||||
|
// The exceptions, named rather than forgotten. Both are shown when the
|
||||||
|
// application may not be able to serve a built stylesheet at all — mid
|
||||||
|
// deploy, or freshly installed — and @vite would throw where the whole
|
||||||
|
// point is to render something reassuring.
|
||||||
|
foreach (['coming-soon.blade.php', 'status.blade.php'] as $page) {
|
||||||
|
$source = File::get(resource_path('views/'.$page));
|
||||||
|
|
||||||
|
// The CALL, not the word: coming-soon.blade.php explains in a comment
|
||||||
|
// why it does not use @vite, and a plain substring match reads that
|
||||||
|
// explanation as the offence it warns about.
|
||||||
|
expect($source)->not->toMatch('/@vite\s*\(/')
|
||||||
|
->and($source)->toContain('<style>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not leave the old marketing stylesheet behind anywhere', function () {
|
||||||
|
// The tell of the previous edition: warm paper and the 3px radius. Left in
|
||||||
|
// a partial nobody opened, it would come back the next time somebody
|
||||||
|
// copied a section.
|
||||||
|
foreach (File::allFiles(resource_path('views')) as $file) {
|
||||||
|
if (in_array($file->getFilename(), ['coming-soon.blade.php', 'status.blade.php'], true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The mail layout is deliberately inline-styled: an email client has no
|
||||||
|
// stylesheet of ours to load.
|
||||||
|
if (str_contains($file->getPathname(), '/mail/') || str_contains($file->getPathname(), '/pdf/')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(File::get($file->getPathname()))
|
||||||
|
->not->toContain('--paper')
|
||||||
|
->not->toContain('Plex Serif');
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue