Compare commits

..
7 Commits
Author SHA1 Message Date
kuba 26224b2ab7 Optzimize responsive styling 2026-09-24 15:23:03 +02:00
kuba fff248f25f Unnecessary transition for header bg 2026-09-24 14:55:38 +02:00
kuba 6897103041 Simple homepage 2026-09-24 14:37:40 +02:00
kuba d4769e9a33 Fix missing preview for images 2026-09-24 14:18:45 +02:00
kuba 061f94c85f Repair logos 2026-09-24 13:59:36 +02:00
kuba 5ff2867a26 Cleanup after cloned content method 2026-09-24 13:42:26 +02:00
kuba 8e3cbb6530 Preview styles 2026-09-24 12:54:30 +02:00
25 changed files with 660 additions and 385 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ dist/
.astro/ .astro/
.DS_Store .DS_Store
# Content lives in the ctao/content repo (build overlays it; scripts/link-content.sh locally) # Content lives in the ctao/content repo (deploy copies it in; npm run content:link links ../content locally)
src/content/news/ src/content/news/
src/content/pages/ src/content/pages/
public/uploads/ public/uploads/
+33 -89
View File
@@ -1,110 +1,54 @@
# CTAO Science Portal — simple proof of concept # CTAO Science Portal
The `main` branch uses the sibling `mockup` project's layout, assets, CSS, Static Astro site with a git-based CMS (Sveltia). Code lives here; editorial
and header interactions. Astro generates a static site. Sveltia CMS edits content lives in the separate [`ctao/content`](https://astro-git.isl-dev.grid.cyfronet.pl/ctao/content)
exactly four Markdown pages in the separate `ctao/content` repository. repository. The Home page and four Example pages are editable; the rest are placeholders.
## Scope - Live demo: https://astro.isl-dev.grid.cyfronet.pl
- Editor: https://astro.isl-dev.grid.cyfronet.pl/admin/ (Gitea sign-in)
| Example submenu | URL | Content file |
|---|---|---|
| Lorem ipsum | `/example/lorem-ipsum/` | `pages/lorem-ipsum.md` |
| Dolor sit amet | `/example/dolor-sit-amet/` | `pages/dolor-sit-amet.md` |
| Consectetur adipiscing | `/example/consectetur-adipiscing/` | `pages/consectetur-adipiscing.md` |
| Sed do eiusmod | `/example/sed-do-eiusmod/` | `pages/sed-do-eiusmod.md` |
Home, News, Data, Proposals, Dashboard, Sign in, Search, Support, Privacy,
Disclaimer, and Contact have the shared header/footer, a title, and an empty
body. Search submits to its placeholder. Only English is available; the
other language options are inactive. There are no news articles, news
pagination, RSS feeds, search index, service integrations, or public sign-in.
`/admin/` remains the working CMS entry point.
## Run locally ## Run locally
Use Node 22.19 or newer (deployment uses Node 24). Node 22.19 or newer. Clone the content repository next to this one:
```sh ```sh
git clone https://astro-git.isl-dev.grid.cyfronet.pl/ctao/content.git ../content
npm ci npm ci
npm run dev npm run dev # http://localhost:4321
``` ```
Open `http://localhost:4321/example/lorem-ipsum/` for the full mockup example. The site reads `../content` directly through links, so pull that repository
The first run copies bundled `sample-content/pages/` and `uploads/` into the for the latest content. To use a checkout elsewhere:
ignored runtime directories. Existing content is preserved. An incomplete `npm run content:link -- <path>`.
existing set fails with a missing-file error instead of silently mixing
sample content with editorial content.
```sh Before pushing code, run `npm run check` (types, build, internal links).
npm run content:setup # explicitly restore the four sample pages
npm run content:sync -- ../ctao-content # copy from a separate content checkout
npm run check # types, static build, internal links
npm run build
npm run preview # inspect the built site
```
`content:setup` and `content:sync` replace the four runtime Markdown files and ## Edit content locally
copy uploads, overwriting same-named media. They do not modify the source
content checkout. Only the four allowlisted pages are loaded and rendered;
unrelated Markdown files never create public routes.
## Edit with Sveltia locally, without Gitea With `npm run dev` running, open `http://localhost:4321/admin/` in Chrome or
Edge, choose **Work with Local Repository** and select the `../content` folder.
Saves write the Markdown files and show up on the local site on reload. They
are not committed: commit and push in `../content` to publish.
```sh ## Publishing
npm run content:local
npm run dev
```
In Chrome or Edge, open `/admin/`, select **Work with Local Repository**, and Pushing to `main` of `ctao/portal` (code) or `ctao/content` (content) on
choose this project's `.tmp/content` directory. The setup command creates a Gitea rebuilds and publishes the live demo automatically; the online editor
local Git repository there, seeded from the bundled content. Re-running it commits to `ctao/content` directly. Pushing to Bitbucket does not deploy.
preserves edits in that directory. Server setup is in [deploy/README.md](deploy/README.md).
Save an Example page, then run this in another terminal to refresh the site:
```sh
npm run content:sync -- .tmp/content
```
The local editor writes files without committing them. This follows
[Sveltia's local workflow](https://sveltiacms.app/en/docs/workflows/local).
Choose the content repository root, not the portal code repository root:
the CMS expects `pages/` and `uploads/` directly beneath it.
## Content and publishing
The CMS uses a [fixed file collection](https://sveltiacms.app/en/docs/collections/files).
Editors can change title, introduction, optional image/alternative text/caption,
Markdown body, and optional button label/destination. Both button fields are
needed to display the button. A Markdown blockquote receives the mockup's
highlight styling. Images without alternative text are treated as decorative.
Menu labels, ordering, URLs, and the shared site layout remain code-controlled.
Remote editing uses Gitea OAuth and saves to **`ctao/content`, branch `main`**.
That branch must contain the four files from `sample-content/pages/` and the
sample upload before publishing this version. The code mirror is
**`ctao/portal`, branch `main`**. Deployment independently selects
`CODE_BRANCH` and `CONTENT_BRANCH`, overlays content, runs checks, and publishes
atomically. `CONTENT_BRANCH` must match `backend.branch` in the CMS config.
See [deploy/README.md](deploy/README.md) for migration and publishing steps.
A push to Bitbucket alone does not deploy the site.
## Where things live ## Where things live
| Path | Purpose | | Path | Purpose |
|---|---| |---|---|
| `src/layouts/Base.astro` | Mockup header, navigation, footer, page shell | | `src/layouts/Base.astro` | Header, navigation, footer, page shell |
| `src/components/PageHero.astro` | Breadcrumb, title, introduction, nebula | | `src/layouts/ContentPage.astro` | Layout of the editable pages (Home and Examples) |
| `src/pages/example/[slug].astro` | One reusable editable-page template | | `src/data/examples.json` | Example pages: menu labels, order, slugs |
| `src/data/examples.json` | Fixed submenu labels, order, and slugs | | `src/data/placeholders.json` | Placeholder pages |
| `src/data/placeholders.json` | Empty destinations | | `src/styles/global.css` | All site styles |
| `src/styles/global.css` | Mockup styles plus Markdown/placeholder variants | | `public/admin/config.yml` | CMS fields and Gitea connection |
| `public/assets/`, `public/header.js` | Mockup assets and menu behavior | | `scripts/preview-styles.mjs` | Builds the CMS preview styles from `global.css` |
| `sample-content/` | Versioned seed content for local use and Gitea setup | | `scripts/content.mjs` | Links the content checkout into the site |
| `src/content/pages/`, `public/uploads/` | Ignored runtime content overlay |
| `public/admin/config.yml` | Four fixed CMS files and Gitea configuration |
| `scripts/preview-styles.mjs` | Generates CMS body preview styles from site CSS |
The vendored CMS bundle remains in `public/vendor/sveltia-cms.js`. Dependencies To add an Example page, add it to `src/data/examples.json` and
remain pinned; this simplification adds no application dependencies. `public/admin/config.yml`, and commit its Markdown file to `ctao/content`.
+7 -10
View File
@@ -31,13 +31,11 @@ missing). Everything else runs inside containers.
## Publish the simplified portal from `main` ## Publish the simplified portal from `main`
1. In a checkout of `ctao/content`, switch to `main`. Copy the missing 1. Check that `ctao/content:main` contains the four Example files in `pages/`
Example files from `sample-content/pages/` and (listed in `src/data/examples.json`) and the uploads they reference. They
`sample-content/uploads/hero.jpg` from this code repository into its root are already committed there (`a3452d7`, "demo lorem ipsum pages"). News and
`pages/` and `uploads/` directories, then commit the seed files. Preserve other page files can remain in the content repository; this portal loads
any existing editorial versions of these files. Existing only the four Example files.
news or other page files can remain in the content history; this portal
loads only the four Example files.
2. Push the content branch to Gitea as `ctao/content:main`, and the code 2. Push the content branch to Gitea as `ctao/content:main`, and the code
branch as `ctao/portal:main`. These are separate pushes; Bitbucket is branch as `ctao/portal:main`. These are separate pushes; Bitbucket is
not the deployment remote. not the deployment remote.
@@ -90,9 +88,8 @@ the previous portal may be restored.
password never lands in a file or shell history: password never lands in a file or shell history:
`podman exec -it ctao-demo-gitea gitea admin user create --admin --username <you> --email <you@…> --random-password` `podman exec -it ctao-demo-gitea gitea admin user create --admin --username <you> --email <you@…> --random-password`
7. **[W]** In the Gitea UI: create org `ctao` with repos `portal` and 7. **[W]** In the Gitea UI: create org `ctao` with repos `portal` and
`content` (both public read). Seed the content `main` branch with `content` (both public read). The content `main` branch needs the four
`sample-content/pages/` and `sample-content/uploads/` as described above. Example pages described above. Then, from your workstation, push both over
Then, from your workstation, push both over
the vhost with a repo-scoped token: the vhost with a repo-scoped token:
`git push https://<user>:<token>@astro-git.isl-dev.grid.cyfronet.pl/ctao/portal.git main` `git push https://<user>:<token>@astro-git.isl-dev.grid.cyfronet.pl/ctao/portal.git main`
`git push https://<user>:<token>@astro-git.isl-dev.grid.cyfronet.pl/ctao/content.git main` `git push https://<user>:<token>@astro-git.isl-dev.grid.cyfronet.pl/ctao/content.git main`
+4 -6
View File
@@ -4,15 +4,13 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"content:setup": "node scripts/content.mjs", "content:link": "node scripts/content.mjs --relink",
"content:sync": "node scripts/content.mjs", "predev": "node scripts/content.mjs && node scripts/preview-styles.mjs",
"content:local": "node scripts/local-content.mjs",
"predev": "node scripts/content.mjs --ensure && node scripts/preview-styles.mjs",
"dev": "astro dev", "dev": "astro dev",
"prebuild": "node scripts/content.mjs --ensure && node scripts/preview-styles.mjs", "prebuild": "node scripts/content.mjs && node scripts/preview-styles.mjs",
"build": "astro build", "build": "astro build",
"preview": "astro preview", "preview": "astro preview",
"precheck": "node scripts/content.mjs --ensure && node scripts/preview-styles.mjs", "precheck": "node scripts/content.mjs && node scripts/preview-styles.mjs",
"check": "astro check && astro build && node scripts/check-links.mjs" "check": "astro check && astro build && node scripts/check-links.mjs"
}, },
"dependencies": { "dependencies": {
+24 -1
View File
@@ -5,7 +5,6 @@
backend: backend:
name: gitea name: gitea
# Keep this branch in sync with CONTENT_BRANCH in deploy/build.sh. # Keep this branch in sync with CONTENT_BRANCH in deploy/build.sh.
# Seed the content branch from sample-content/ before deploying.
repo: ctao/content repo: ctao/content
branch: main branch: main
base_url: https://astro-git.isl-dev.grid.cyfronet.pl base_url: https://astro-git.isl-dev.grid.cyfronet.pl
@@ -45,6 +44,30 @@ media_libraries:
optimize: true optimize: true
collections: collections:
- name: home
label: "Home page"
files:
- name: home
label: "Home page"
file: "pages/home.md"
format: frontmatter
fields:
- { name: title, label: "Title", widget: string }
- { name: introduction, label: "Introduction", widget: text, required: false }
- name: images
label: "Images"
label_singular: "image"
widget: list
required: false
max: 2
hint: "Up to two images, shown side by side."
fields:
- { name: image, label: "Image", widget: image }
- { name: alt, label: "Alternative text", widget: string, required: false, hint: "Describe the image; leave empty only for decorative images." }
- { name: caption, label: "Caption", widget: string, required: false }
- { name: body, label: "Body", widget: markdown }
- { name: buttonLabel, label: "Button label", widget: string, required: false }
- { name: buttonUrl, label: "Button destination", widget: string, required: false, hint: "A site path such as /example/lorem-ipsum/, or an https:// URL." }
- name: examples - name: examples
label: "Example pages" label: "Example pages"
files: files:
+261 -23
View File
@@ -1,7 +1,10 @@
/* Generated by scripts/preview-styles.mjs; edit src/styles/global.css. */ /* Generated by scripts/preview-styles.mjs; edit src/styles/global.css. */
/* ========================================================================== /* ==========================================================================
CTAO Science Portal: content page mockup CTAO Science Portal: content page mockup
Colours and type follow the CTAO Brand Guidelines (April 2024). Colours and type follow the CTAO Brand Guidelines (April 2024).
The editor preview (public/admin/preview.css) is generated from the
hero, prose and button sections below by scripts/preview-styles.mjs.
========================================================================== */ ========================================================================== */
/* Fonts /* Fonts
@@ -50,9 +53,12 @@
--gutter: clamp(1rem, 4vw, 2.5rem); --gutter: clamp(1rem, 4vw, 2.5rem);
--header-height: 7rem; --header-height: 7rem;
--header-height-scrolled: 5rem; --header-height-scrolled: 5rem;
--radius-lg: 60px; /* Large decorative corners (hero, header, search panel): grows with the
--radius-md: 30px; viewport instead of switching at breakpoints */
--radius-lg: min(15vw, 180px);
--radius-sm: 15px; --radius-sm: 15px;
/* How far photos are pulled up into the hero */
--hero-overlap: clamp(6rem, 11.25vw, 9rem);
/* Dropdown submenus (Example, language) */ /* Dropdown submenus (Example, language) */
--radius-menu: 8px; --radius-menu: 8px;
--transition: 0.3s ease-in-out; --transition: 0.3s ease-in-out;
@@ -126,19 +132,182 @@ button {
outline-color: var(--cherenkov-blue); outline-color: var(--cherenkov-blue);
} }
/* Utilities
========================================================================== */
.container {
width: min(100% - 2 * var(--gutter), var(--content-width));
margin-inline: auto;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.skip-link {
position: absolute;
top: -4rem;
left: 1rem;
z-index: 100;
padding: 0.5rem 1rem;
color: var(--galaxy-blue);
background: var(--white);
border-radius: var(--radius-sm);
}
.skip-link:focus {
top: 1rem;
}
.arrow {
display: inline-block;
transition: transform 0.25s ease;
}
a:hover .arrow,
button:hover .arrow {
transform: translateX(4px);
}
@keyframes panel-in {
from {
opacity: 0;
transform: translateY(-6px);
}
to {
opacity: 1;
transform: none;
}
}
/* Page hero
========================================================================== */
.page-hero {
margin-top: calc(-1 * var(--header-height));
padding: calc(var(--header-height) + 3rem) 0 calc(var(--hero-overlap) + 3rem);
color: var(--white);
background: var(--grain), var(--galaxy-blue);
border-bottom-right-radius: var(--radius-lg);
}
/* Pages without a photo: no room needed for the figure to overlap */
.page-hero--compact {
padding-bottom: 4rem;
}
.breadcrumb ol {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 0 0 1rem;
padding: 0;
font-size: 0.875rem;
list-style: none;
}
.breadcrumb li + li::before {
content: "›";
margin-right: 0.5rem;
color: rgba(255, 255, 255, 0.5);
}
.breadcrumb a {
color: rgba(255, 255, 255, 0.6);
}
.breadcrumb a:hover {
color: var(--white);
}
.breadcrumb [aria-current] {
font-weight: 500;
color: var(--white);
}
/* Headline: Space Grotesk medium, white on dark (D.3.4) */
.page-hero__title {
max-width: 24ch;
margin: 0 0 1.25rem;
font-family: var(--font-head);
font-size: clamp(2rem, 5vw, 3.75rem);
font-weight: 500;
line-height: 1.05;
letter-spacing: -0.02em;
text-wrap: balance;
overflow-wrap: anywhere;
}
.page-hero__lead {
max-width: 70ch;
margin: 0;
font-size: 1.0625rem;
font-weight: 500;
line-height: 1.4;
}
/* Photo pulled up over the hero; flat, no shadow */
.page-figure {
margin: calc(-1 * var(--hero-overlap)) 0 4rem;
}
.page-figure img {
width: 100%;
border-radius: var(--radius-sm);
}
.page-figure figcaption {
margin-top: 0.75rem;
font-size: 0.8125rem;
color: var(--text-muted);
}
/* Two photos side by side (Home), pulled up over the hero together; they
stack by themselves once each would be narrower than 18rem */
.page-figures {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: 1.5rem;
margin: calc(-1 * var(--hero-overlap)) 0 4rem;
}
.page-figures .page-figure {
margin: 0;
}
/* Same shape for both photos, whatever the uploads' proportions */
.page-figures img {
aspect-ratio: 3 / 2;
object-fit: cover;
}
/* Prose /* Prose
========================================================================== */ ========================================================================== */
body { .prose {
padding-bottom: 5rem; padding-bottom: 5rem;
overflow-wrap: anywhere;
} }
body > * { .prose--no-image {
padding-top: 4rem;
}
.prose > * {
max-width: 64rem; max-width: 64rem;
} }
/* Galaxy Blue headline on light background (D.3.4) */ /* Galaxy Blue headline on light background (D.3.4) */
body h2 { .prose h2 {
margin: 3rem 0 1rem; margin: 3rem 0 1rem;
font-family: var(--font-head); font-family: var(--font-head);
font-size: clamp(1.75rem, 3vw, 2.375rem); font-size: clamp(1.75rem, 3vw, 2.375rem);
@@ -148,26 +317,26 @@ body h2 {
color: var(--galaxy-blue); color: var(--galaxy-blue);
} }
body h2:first-child { .prose h2:first-child {
margin-top: 0; margin-top: 0;
} }
body p { .prose p {
margin: 0 0 1.25rem; margin: 0 0 1.25rem;
font-size: 1.0625rem; font-size: 1.0625rem;
line-height: 1.6; line-height: 1.6;
} }
/* Text links: colour only at rest, underline on hover and focus */ /* Text links: colour only at rest, underline on hover and focus */
body a:not(.btn):hover, .prose a:not(.btn):hover,
body a:not(.btn):focus-visible { .prose a:not(.btn):focus-visible {
text-decoration: underline; text-decoration: underline;
text-underline-offset: 0.15em; text-underline-offset: 0.15em;
} }
/* Cherenkov Blue fails contrast as text on white, so it is the accent bar */ /* Cherenkov Blue fails contrast as text on white, so it is the accent bar */
body .highlight, .prose .highlight,
body blockquote { .prose blockquote {
margin: 2.5rem 0; margin: 2.5rem 0;
padding-left: 1.5rem; padding-left: 1.5rem;
font-size: 1.375rem; font-size: 1.375rem;
@@ -177,6 +346,61 @@ body blockquote {
border-left: 4px solid var(--cherenkov-blue); border-left: 4px solid var(--cherenkov-blue);
} }
.prose blockquote p {
font-size: inherit;
line-height: inherit;
}
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
font-family: var(--font-head);
color: var(--galaxy-blue);
}
.prose li {
margin-bottom: 0.5rem;
}
.prose img {
border-radius: var(--radius-sm);
}
.prose pre {
padding: 1rem;
overflow-x: auto;
background: var(--moon-gray);
}
.prose code {
overflow-wrap: normal;
}
.prose table {
display: block;
overflow-x: auto;
border-collapse: collapse;
}
.prose th,
.prose td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #ddd;
}
.prose hr {
margin: 2rem 0;
border: 0;
border-top: 1px solid #ddd;
}
/* Placeholder pages: empty body until their content exists */
.placeholder-body {
min-height: 12rem;
}
/* Buttons /* Buttons
========================================================================== */ ========================================================================== */
@@ -217,16 +441,30 @@ body blockquote {
filter: brightness(1.1); filter: brightness(1.1);
} }
body { overflow-wrap: anywhere; } /* Preferences
body h3, body h4, body h5, body h6 { color: var(--galaxy-blue); font-family: var(--font-head); } ========================================================================== */
body li { margin-bottom: 0.5rem; }
body blockquote p { font-size: inherit; line-height: inherit; }
body pre { padding: 1rem; overflow-x: auto; background: var(--moon-gray); }
body code { overflow-wrap: normal; }
body table { display: block; overflow-x: auto; border-collapse: collapse; }
body th, body td { padding: 0.75rem; border-bottom: 1px solid #ddd; text-align: left; }
body hr { margin: 2rem 0; border: 0; border-top: 1px solid #ddd; }
body img { border-radius: var(--radius-sm); }
body { display: block; min-height: 0; max-width: 64rem; margin: 0 auto; padding: 1.5rem; } /* Windows High Contrast: drop the decorative hero background behind system text */
h1 { color: var(--galaxy-blue); font-family: var(--font-head); font-weight: 500; line-height: 1.05; } @media (forced-colors: active) {
.page-hero {
background-image: none;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation: none !important;
transition: none !important;
}
}
/* Preview frame
========================================================================== */
/* There is no site header above the hero */
.page-hero {
margin-top: 0;
padding-top: 3rem;
}
+2 -2
View File
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" id="Ebene_1" x="0" y="0" viewBox="0 0 153 43.9"> <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" id="Ebene_1" x="0" y="0" viewBox="0 0 153 46.0">
<style> <style>
.st3{fill:#ffffff} .st3{fill:#ffffff}
.st5{fill:#00E4D8} .st5{fill:#00E4D8}
@@ -7,5 +7,5 @@
<path d="m100 22.3 4.3 8.4h9.2l-5.3-10.2zm22.4-6.2V16c0-5.2 2.9-8.2 10.2-8.2 5.7 0 8.7 1.9 9.8 5.2l8.8-1.4c-1.8-7-8.2-11-18.6-11-12.4 0-19.1 5.7-19.1 15.5 0 .5 0 1.1.1 1.6zm20.1-2.7c.2.8.3 1.7.3 2.6 0 5.1-3 8.3-10.2 8.3-6.4 0-9.5-2.5-10.1-6.7l-8.7 1.8c1.4 7.7 7.9 12.2 18.8 12.2 12.4 0 19.1-5.8 19.1-15.6 0-1.6-.2-3.1-.5-4.4zM73.4 26.9l-2 3.8h9.2l2.7-5.4zm25-25.6H86.6L74.5 24.7l10.2-2.2 7.8-15.2 6.4 12.6 8.3-1.3zM53.7 30.1v.6h8.5v-2zm0-1.1 8.5-1.7V8.5h13.4V1.3H40.2v7.2h13.5zM19.6.5c10.8 0 17 4.7 17.8 12.2h-9.1c-.6-2.7-3.3-5-8.8-5-6.3 0-9.4 3.1-9.4 8.2s3.1 8.3 9.4 8.3c5.4 0 8.2-2.3 8.8-5.1h9.1c-.9 7.6-7 12.3-17.8 12.3C7.8 31.5 1.3 26.1 1.3 16S7.8.5 19.6.5" class="st3"/> <path d="m100 22.3 4.3 8.4h9.2l-5.3-10.2zm22.4-6.2V16c0-5.2 2.9-8.2 10.2-8.2 5.7 0 8.7 1.9 9.8 5.2l8.8-1.4c-1.8-7-8.2-11-18.6-11-12.4 0-19.1 5.7-19.1 15.5 0 .5 0 1.1.1 1.6zm20.1-2.7c.2.8.3 1.7.3 2.6 0 5.1-3 8.3-10.2 8.3-6.4 0-9.5-2.5-10.1-6.7l-8.7 1.8c1.4 7.7 7.9 12.2 18.8 12.2 12.4 0 19.1-5.8 19.1-15.6 0-1.6-.2-3.1-.5-4.4zM73.4 26.9l-2 3.8h9.2l2.7-5.4zm25-25.6H86.6L74.5 24.7l10.2-2.2 7.8-15.2 6.4 12.6 8.3-1.3zM53.7 30.1v.6h8.5v-2zm0-1.1 8.5-1.7V8.5h13.4V1.3H40.2v7.2h13.5zM19.6.5c10.8 0 17 4.7 17.8 12.2h-9.1c-.6-2.7-3.3-5-8.8-5-6.3 0-9.4 3.1-9.4 8.2s3.1 8.3 9.4 8.3c5.4 0 8.2-2.3 8.8-5.1h9.1c-.9 7.6-7 12.3-17.8 12.3C7.8 31.5 1.3 26.1 1.3 16S7.8.5 19.6.5" class="st3"/>
<path d="M94.4 23.7c1-1.4 3.3-2.1 5.1-2.7-2 .1-4.4.3-5.8-.6-1.4-1-2.1-3.3-2.7-5.1.1 2 .3 4.4-.6 5.8-1 1.4-3.3 2.1-5.1 2.7 2-.1 4.4-.3 5.8.6 1.4 1 2.1 3.3 2.7 5.1-.2-2-.4-4.4.6-5.8" class="st5"/> <path d="M94.4 23.7c1-1.4 3.3-2.1 5.1-2.7-2 .1-4.4.3-5.8-.6-1.4-1-2.1-3.3-2.7-5.1.1 2 .3 4.4-.6 5.8-1 1.4-3.3 2.1-5.1 2.7 2-.1 4.4-.3 5.8.6 1.4 1 2.1 3.3 2.7 5.1-.2-2-.4-4.4.6-5.8" class="st5"/>
</g> </g>
<path d="M5.6 42.3c-.6 0-1-.1-1.4-.3s-.7-.4-1-.8c-.2-.3-.4-.7-.4-1.2H4c0 .2.1.5.2.6.1.2.3.3.5.4s.5.1.7.1c.3 0 .5 0 .7-.1s.4-.2.5-.4c.3 0 .4-.2.4-.4s-.1-.3-.2-.5-.3-.2-.4-.3c-.2-.1-.4-.2-.7-.2l-.8-.2c-.6-.2-1.1-.4-1.4-.7-.4-.3-.5-.7-.5-1.2 0-.4.1-.8.3-1.1s.5-.6.9-.7.9-.3 1.4-.3 1 .1 1.3.3c.4.2.7.4.9.7s.3.7.3 1.1H6.9c0-.3-.2-.5-.4-.7s-.6-.3-.9-.3-.5 0-.7.1-.3.2-.4.3-.2.3-.2.5.1.4.2.5.3.2.5.3.3.2.5.2l.7.2c.3.1.5.1.8.3s.5.2.7.4.4.4.5.6.2.5.2.8c0 .4-.1.8-.3 1.1s-.5.6-.9.7c-.6.2-1.1.2-1.6.2m11.8 0c-.6 0-1.2-.1-1.7-.4s-.9-.7-1.2-1.3c-.3-.5-.4-1.2-.4-2s.1-1.4.4-2c.3-.5.7-1 1.2-1.3s1-.4 1.7-.4c.4 0 .8.1 1.1.2s.6.3.9.5.5.5.7.8.3.7.3 1h-1.3c0-.2-.1-.4-.2-.6s-.2-.3-.4-.4c-.1-.1-.3-.2-.5-.3s-.4-.1-.6-.1c-.4 0-.7.1-1 .3s-.5.5-.7.9-.3.8-.3 1.4c0 .5.1 1 .3 1.4s.4.7.7.8c.3.2.6.3 1 .3.2 0 .4 0 .6-.1s.4-.1.5-.3c.2-.1.3-.3.4-.4s.2-.4.2-.6h1.3c0 .3-.2.7-.3 1-.2.3-.4.6-.6.8-.3.2-.6.4-.9.5-.4.3-.8.3-1.2.3m10.3-7.2v7.1h-1.3v-7.1zm6.3 7.1v-7.1h4.6v1.1h-3.4v1.9h3.1v1.1h-3.1v2h3.4v1.1H34zm10.9 0v-7.1h1.4l2.5 3.9c.1.2.2.3.3.6s.2.4.4.7c.1.3.2.5.4.8h-.1c0-.3 0-.5-.1-.8s0-.5 0-.8v-4.3H51v7.1h-1.4l-2.2-3.5c-.1-.2-.3-.4-.4-.7-.1-.2-.3-.5-.4-.7l-.5-1h.2c0 .3 0 .7.1 1v4.9h-1.5zm15.3.1c-.6 0-1.2-.1-1.7-.4s-.9-.7-1.2-1.3c-.3-.5-.4-1.2-.4-2s.1-1.4.4-2c.3-.5.7-1 1.2-1.3s1-.4 1.7-.4c.4 0 .8.1 1.1.2s.6.3.9.5.5.5.7.8.3.7.3 1H62c0-.2-.1-.4-.2-.6s-.2-.3-.4-.4c-.1-.1-.3-.2-.5-.3s-.4-.1-.6-.1c-.4 0-.7.1-1 .3s-.5.5-.7.9-.3.8-.3 1.4c0 .5.1 1 .3 1.4s.4.7.7.8c.3.2.6.3 1 .3.2 0 .4 0 .6-.1s.4-.1.5-.3c.2-.1.3-.3.4-.4s.2-.4.2-.6h1.3c0 .3-.2.7-.3 1-.2.3-.4.6-.6.8-.3.2-.6.4-.9.5-.5.3-.8.3-1.3.3m9.1-.1v-7.1H74v1.1h-3.4v1.9h3.1v1.1h-3.1v2H74v1.1h-4.7zm18.2 0v-7.1h2.7c.5 0 1 .1 1.4.3s.7.5.8.8.3.8.3 1.2q0 .75-.3 1.2c-.2.4-.5.6-.9.8s-.8.3-1.4.3h-1.8v-1.1H90c.3 0 .6-.1.8-.2s.4-.3.5-.5.1-.4.1-.7 0-.5-.1-.7-.2-.3-.5-.5-.5-.2-.8-.2h-1.2v6.1h-1.3zm14.3.1c-.6 0-1.2-.1-1.7-.4s-.9-.7-1.2-1.3-.4-1.2-.4-2 .1-1.4.4-2 .7-1 1.2-1.3 1.1-.4 1.7-.4 1.2.1 1.7.4.9.7 1.2 1.3c.3.5.4 1.2.4 2s-.1 1.4-.4 2c-.3.5-.7 1-1.2 1.3s-1.1.4-1.7.4m0-1.1c.4 0 .7-.1 1-.3s.5-.5.7-.9.3-.8.3-1.4c0-.5-.1-1-.3-1.4s-.4-.7-.7-.9-.6-.3-1-.3-.7.1-1 .3-.5.5-.7.9-.3.8-.3 1.4c0 .5.1 1 .3 1.4s.4.7.7.9.6.3 1 .3m9.4 1v-7.1h2.7c.5 0 1 .1 1.4.3s.7.5.8.8.3.7.3 1.2-.1.8-.3 1.2c-.2.3-.5.6-.9.8s-.8.3-1.4.3h-1.9v-1.1h1.7c.3 0 .6 0 .8-.1s.4-.2.5-.4.1-.4.1-.6c0-.3 0-.5-.1-.7s-.3-.3-.5-.4-.5-.1-.8-.1h-1.2v6.1h-1.2zm4 0-1.7-3.2h1.4l1.8 3.2zm6.9-6v-1.1h5.7v1.1h-2.2v6.1h-1.3v-6.1zm10.4 6 2.5-7.1h1.6l2.5 7.1h-1.4l-1.3-3.7c-.1-.4-.3-.8-.4-1.3s-.3-1-.4-1.6h.3c-.2.6-.3 1.2-.4 1.7s-.3.9-.4 1.3l-1.2 3.7h-1.4zm1.5-1.7v-1h3.8v1zm11 1.7v-7.1h1.3v6.1h3.1v1.1H145z" class="st3"/> <path d="M6.48 45.77Q5.41 45.77 4.62 45.44Q3.83 45.1 3.38 44.47Q2.94 43.83 2.9 42.92H4.53Q4.57 43.4 4.83 43.72Q5.09 44.04 5.52 44.2Q5.95 44.36 6.47 44.36Q7.01 44.36 7.43 44.19Q7.85 44.03 8.08 43.72Q8.32 43.42 8.32 43.03Q8.32 42.67 8.11 42.43Q7.9 42.2 7.53 42.03Q7.15 41.87 6.65 41.75L5.6 41.47Q4.45 41.18 3.8 40.57Q3.15 39.97 3.15 38.99Q3.15 38.18 3.59 37.57Q4.03 36.95 4.8 36.61Q5.56 36.28 6.52 36.28Q7.51 36.28 8.25 36.62Q8.99 36.96 9.41 37.56Q9.83 38.16 9.85 38.93H8.25Q8.18 38.33 7.71 38.01Q7.25 37.68 6.5 37.68Q5.98 37.68 5.6 37.84Q5.23 38 5.03 38.27Q4.83 38.55 4.83 38.9Q4.83 39.28 5.06 39.53Q5.3 39.78 5.66 39.93Q6.03 40.08 6.4 40.17L7.26 40.39Q7.77 40.51 8.26 40.72Q8.74 40.92 9.13 41.24Q9.52 41.55 9.75 41.99Q9.97 42.44 9.97 43.04Q9.97 43.85 9.56 44.47Q9.15 45.08 8.37 45.43Q7.58 45.77 6.48 45.77ZM18.24 45.75Q17.03 45.75 16.08 45.19Q15.12 44.63 14.57 43.57Q14.02 42.51 14.02 41.02Q14.02 39.53 14.58 38.47Q15.13 37.4 16.08 36.84Q17.04 36.28 18.24 36.28Q19 36.28 19.66 36.49Q20.32 36.71 20.84 37.12Q21.35 37.53 21.69 38.12Q22.02 38.72 22.13 39.47H20.47Q20.39 39.06 20.19 38.74Q20 38.43 19.71 38.21Q19.41 37.98 19.05 37.87Q18.68 37.76 18.27 37.76Q17.51 37.76 16.93 38.14Q16.34 38.52 16.01 39.25Q15.68 39.97 15.68 41.02Q15.68 42.08 16.02 42.8Q16.35 43.53 16.93 43.9Q17.52 44.27 18.27 44.27Q18.68 44.27 19.04 44.16Q19.41 44.04 19.7 43.82Q20 43.6 20.19 43.28Q20.39 42.97 20.47 42.56H22.14Q22.05 43.22 21.73 43.8Q21.42 44.38 20.91 44.82Q20.41 45.26 19.74 45.51Q19.06 45.75 18.24 45.75ZM28.13 36.4V45.63H26.48V36.4ZM32.81 45.63V36.4H38.81V37.79H34.46V40.28H38.49V41.66H34.46V44.24H38.83V45.63ZM43.31 45.63V36.4H45.18L48.38 41.48Q48.57 41.8 48.8 42.2Q49.02 42.6 49.26 43.09Q49.5 43.58 49.74 44.19H49.55Q49.51 43.66 49.48 43.12Q49.44 42.57 49.43 42.07Q49.41 41.58 49.41 41.24V36.4H51.09V45.63H49.21L46.33 41.05Q46.06 40.61 45.82 40.19Q45.57 39.76 45.3 39.22Q45.02 38.68 44.62 37.92H44.85Q44.89 38.59 44.92 39.2Q44.96 39.8 44.98 40.28Q45 40.75 45 41.05V45.63ZM59.7 45.75Q58.49 45.75 57.53 45.19Q56.58 44.63 56.03 43.57Q55.48 42.51 55.48 41.02Q55.48 39.53 56.03 38.47Q56.58 37.4 57.54 36.84Q58.5 36.28 59.7 36.28Q60.46 36.28 61.12 36.49Q61.78 36.71 62.29 37.12Q62.81 37.53 63.14 38.12Q63.48 38.72 63.59 39.47H61.92Q61.84 39.06 61.65 38.74Q61.45 38.43 61.16 38.21Q60.87 37.98 60.51 37.87Q60.14 37.76 59.73 37.76Q58.97 37.76 58.38 38.14Q57.8 38.52 57.47 39.25Q57.14 39.97 57.14 41.02Q57.14 42.08 57.47 42.8Q57.81 43.53 58.39 43.9Q58.97 44.27 59.72 44.27Q60.14 44.27 60.5 44.16Q60.86 44.04 61.16 43.82Q61.45 43.6 61.65 43.28Q61.85 42.97 61.93 42.56H63.59Q63.5 43.22 63.19 43.8Q62.88 44.38 62.37 44.82Q61.86 45.26 61.19 45.51Q60.52 45.75 59.7 45.75ZM67.93 45.63V36.4H73.93V37.79H69.59V40.28H73.61V41.66H69.59V44.24H73.95V45.63ZM84.45 45.63V36.4H87.91Q88.97 36.4 89.69 36.8Q90.41 37.2 90.78 37.89Q91.15 38.57 91.15 39.46Q91.15 40.34 90.78 41.03Q90.41 41.72 89.68 42.11Q88.95 42.51 87.88 42.51H85.59V41.15H87.66Q88.29 41.15 88.69 40.93Q89.09 40.71 89.28 40.33Q89.46 39.95 89.46 39.46Q89.46 38.96 89.28 38.59Q89.09 38.21 88.69 38Q88.28 37.78 87.65 37.78H86.11V45.63ZM99.41 45.75Q98.2 45.75 97.24 45.19Q96.28 44.63 95.72 43.57Q95.17 42.51 95.17 41.02Q95.17 39.53 95.72 38.47Q96.28 37.4 97.24 36.84Q98.2 36.28 99.41 36.28Q100.62 36.28 101.57 36.84Q102.53 37.4 103.08 38.47Q103.64 39.53 103.64 41.02Q103.64 42.51 103.08 43.57Q102.53 44.63 101.57 45.19Q100.62 45.75 99.41 45.75ZM99.41 44.27Q100.16 44.27 100.74 43.9Q101.32 43.52 101.65 42.8Q101.98 42.07 101.98 41.02Q101.98 39.96 101.65 39.23Q101.32 38.51 100.74 38.13Q100.16 37.76 99.41 37.76Q98.65 37.76 98.07 38.13Q97.49 38.51 97.16 39.24Q96.83 39.97 96.83 41.02Q96.83 42.07 97.16 42.8Q97.49 43.52 98.07 43.9Q98.65 44.27 99.41 44.27ZM108.03 45.63V36.4H111.48Q112.54 36.4 113.26 36.77Q113.99 37.14 114.36 37.81Q114.73 38.47 114.73 39.35Q114.73 40.24 114.35 40.89Q113.98 41.54 113.25 41.89Q112.52 42.24 111.45 42.24H108.99V40.87H111.23Q111.86 40.87 112.26 40.69Q112.66 40.52 112.85 40.18Q113.04 39.84 113.04 39.35Q113.04 38.86 112.85 38.51Q112.66 38.16 112.26 37.97Q111.86 37.78 111.23 37.78H109.68V45.63ZM113.22 45.63 110.98 41.44H112.79L115.07 45.63ZM118.71 37.79V36.4H126.06V37.79H123.21V45.63H121.56V37.79ZM129.7 45.63 132.92 36.4H135.02L138.31 45.63H136.5L134.86 40.84Q134.62 40.09 134.36 39.15Q134.1 38.22 133.78 37.02H134.13Q133.82 38.23 133.57 39.17Q133.32 40.12 133.09 40.84L131.51 45.63ZM131.58 43.34V42.01H136.44V43.34ZM142.37 45.63V36.4H144.02V44.24H148.1V45.63Z" class="st3"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

+2 -2
View File
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" id="Ebene_1" x="0" y="0" viewBox="0 0 153 43.9"> <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" id="Ebene_1" x="0" y="0" viewBox="0 0 153 46.0">
<style> <style>
.st3{fill:#00004a} .st3{fill:#00004a}
</style> </style>
@@ -6,5 +6,5 @@
<path d="m100 22.3 4.3 8.4h9.2l-5.3-10.2zm22.4-6.2V16c0-5.2 2.9-8.2 10.2-8.2 5.7 0 8.7 1.9 9.8 5.2l8.8-1.4c-1.8-7-8.2-11-18.6-11-12.4 0-19.1 5.7-19.1 15.5 0 .5 0 1.1.1 1.6zm20.1-2.7c.2.8.3 1.7.3 2.6 0 5.1-3 8.3-10.2 8.3-6.4 0-9.5-2.5-10.1-6.7l-8.7 1.8c1.4 7.7 7.9 12.2 18.8 12.2 12.4 0 19.1-5.8 19.1-15.6 0-1.6-.2-3.1-.5-4.4zM73.4 26.9l-2 3.8h9.2l2.7-5.4zm25-25.6H86.6L74.5 24.7l10.2-2.2 7.8-15.2 6.4 12.6 8.3-1.3zM53.7 30.1v.6h8.5v-2zm0-1.1 8.5-1.7V8.5h13.4V1.3H40.2v7.2h13.5zM19.6.5c10.8 0 17 4.7 17.8 12.2h-9.1c-.6-2.7-3.3-5-8.8-5-6.3 0-9.4 3.1-9.4 8.2s3.1 8.3 9.4 8.3c5.4 0 8.2-2.3 8.8-5.1h9.1c-.9 7.6-7 12.3-17.8 12.3C7.8 31.5 1.3 26.1 1.3 16S7.8.5 19.6.5" class="st3"/> <path d="m100 22.3 4.3 8.4h9.2l-5.3-10.2zm22.4-6.2V16c0-5.2 2.9-8.2 10.2-8.2 5.7 0 8.7 1.9 9.8 5.2l8.8-1.4c-1.8-7-8.2-11-18.6-11-12.4 0-19.1 5.7-19.1 15.5 0 .5 0 1.1.1 1.6zm20.1-2.7c.2.8.3 1.7.3 2.6 0 5.1-3 8.3-10.2 8.3-6.4 0-9.5-2.5-10.1-6.7l-8.7 1.8c1.4 7.7 7.9 12.2 18.8 12.2 12.4 0 19.1-5.8 19.1-15.6 0-1.6-.2-3.1-.5-4.4zM73.4 26.9l-2 3.8h9.2l2.7-5.4zm25-25.6H86.6L74.5 24.7l10.2-2.2 7.8-15.2 6.4 12.6 8.3-1.3zM53.7 30.1v.6h8.5v-2zm0-1.1 8.5-1.7V8.5h13.4V1.3H40.2v7.2h13.5zM19.6.5c10.8 0 17 4.7 17.8 12.2h-9.1c-.6-2.7-3.3-5-8.8-5-6.3 0-9.4 3.1-9.4 8.2s3.1 8.3 9.4 8.3c5.4 0 8.2-2.3 8.8-5.1h9.1c-.9 7.6-7 12.3-17.8 12.3C7.8 31.5 1.3 26.1 1.3 16S7.8.5 19.6.5" class="st3"/>
<path d="M94.4 23.7c1-1.4 3.3-2.1 5.1-2.7-2 .1-4.4.3-5.8-.6-1.4-1-2.1-3.3-2.7-5.1.1 2 .3 4.4-.6 5.8-1 1.4-3.3 2.1-5.1 2.7 2-.1 4.4-.3 5.8.6 1.4 1 2.1 3.3 2.7 5.1-.2-2-.4-4.4.6-5.8" class="st3"/> <path d="M94.4 23.7c1-1.4 3.3-2.1 5.1-2.7-2 .1-4.4.3-5.8-.6-1.4-1-2.1-3.3-2.7-5.1.1 2 .3 4.4-.6 5.8-1 1.4-3.3 2.1-5.1 2.7 2-.1 4.4-.3 5.8.6 1.4 1 2.1 3.3 2.7 5.1-.2-2-.4-4.4.6-5.8" class="st3"/>
</g> </g>
<path d="M5.6 42.3c-.6 0-1-.1-1.4-.3s-.7-.4-1-.8c-.2-.3-.4-.7-.4-1.2H4c0 .2.1.5.2.6.1.2.3.3.5.4s.5.1.7.1c.3 0 .5 0 .7-.1s.4-.2.5-.4c.3 0 .4-.2.4-.4s-.1-.3-.2-.5-.3-.2-.4-.3c-.2-.1-.4-.2-.7-.2l-.8-.2c-.6-.2-1.1-.4-1.4-.7-.4-.3-.5-.7-.5-1.2 0-.4.1-.8.3-1.1s.5-.6.9-.7.9-.3 1.4-.3 1 .1 1.3.3c.4.2.7.4.9.7s.3.7.3 1.1H6.9c0-.3-.2-.5-.4-.7s-.6-.3-.9-.3-.5 0-.7.1-.3.2-.4.3-.2.3-.2.5.1.4.2.5.3.2.5.3.3.2.5.2l.7.2c.3.1.5.1.8.3s.5.2.7.4.4.4.5.6.2.5.2.8c0 .4-.1.8-.3 1.1s-.5.6-.9.7c-.6.2-1.1.2-1.6.2m11.8 0c-.6 0-1.2-.1-1.7-.4s-.9-.7-1.2-1.3c-.3-.5-.4-1.2-.4-2s.1-1.4.4-2c.3-.5.7-1 1.2-1.3s1-.4 1.7-.4c.4 0 .8.1 1.1.2s.6.3.9.5.5.5.7.8.3.7.3 1h-1.3c0-.2-.1-.4-.2-.6s-.2-.3-.4-.4c-.1-.1-.3-.2-.5-.3s-.4-.1-.6-.1c-.4 0-.7.1-1 .3s-.5.5-.7.9-.3.8-.3 1.4c0 .5.1 1 .3 1.4s.4.7.7.8c.3.2.6.3 1 .3.2 0 .4 0 .6-.1s.4-.1.5-.3c.2-.1.3-.3.4-.4s.2-.4.2-.6h1.3c0 .3-.2.7-.3 1-.2.3-.4.6-.6.8-.3.2-.6.4-.9.5-.4.3-.8.3-1.2.3m10.3-7.2v7.1h-1.3v-7.1zm6.3 7.1v-7.1h4.6v1.1h-3.4v1.9h3.1v1.1h-3.1v2h3.4v1.1H34zm10.9 0v-7.1h1.4l2.5 3.9c.1.2.2.3.3.6s.2.4.4.7c.1.3.2.5.4.8h-.1c0-.3 0-.5-.1-.8s0-.5 0-.8v-4.3H51v7.1h-1.4l-2.2-3.5c-.1-.2-.3-.4-.4-.7-.1-.2-.3-.5-.4-.7l-.5-1h.2c0 .3 0 .7.1 1v4.9h-1.5zm15.3.1c-.6 0-1.2-.1-1.7-.4s-.9-.7-1.2-1.3c-.3-.5-.4-1.2-.4-2s.1-1.4.4-2c.3-.5.7-1 1.2-1.3s1-.4 1.7-.4c.4 0 .8.1 1.1.2s.6.3.9.5.5.5.7.8.3.7.3 1H62c0-.2-.1-.4-.2-.6s-.2-.3-.4-.4c-.1-.1-.3-.2-.5-.3s-.4-.1-.6-.1c-.4 0-.7.1-1 .3s-.5.5-.7.9-.3.8-.3 1.4c0 .5.1 1 .3 1.4s.4.7.7.8c.3.2.6.3 1 .3.2 0 .4 0 .6-.1s.4-.1.5-.3c.2-.1.3-.3.4-.4s.2-.4.2-.6h1.3c0 .3-.2.7-.3 1-.2.3-.4.6-.6.8-.3.2-.6.4-.9.5-.5.3-.8.3-1.3.3m9.1-.1v-7.1H74v1.1h-3.4v1.9h3.1v1.1h-3.1v2H74v1.1h-4.7zm18.2 0v-7.1h2.7c.5 0 1 .1 1.4.3s.7.5.8.8.3.8.3 1.2q0 .75-.3 1.2c-.2.4-.5.6-.9.8s-.8.3-1.4.3h-1.8v-1.1H90c.3 0 .6-.1.8-.2s.4-.3.5-.5.1-.4.1-.7 0-.5-.1-.7-.2-.3-.5-.5-.5-.2-.8-.2h-1.2v6.1h-1.3zm14.3.1c-.6 0-1.2-.1-1.7-.4s-.9-.7-1.2-1.3-.4-1.2-.4-2 .1-1.4.4-2 .7-1 1.2-1.3 1.1-.4 1.7-.4 1.2.1 1.7.4.9.7 1.2 1.3c.3.5.4 1.2.4 2s-.1 1.4-.4 2c-.3.5-.7 1-1.2 1.3s-1.1.4-1.7.4m0-1.1c.4 0 .7-.1 1-.3s.5-.5.7-.9.3-.8.3-1.4c0-.5-.1-1-.3-1.4s-.4-.7-.7-.9-.6-.3-1-.3-.7.1-1 .3-.5.5-.7.9-.3.8-.3 1.4c0 .5.1 1 .3 1.4s.4.7.7.9.6.3 1 .3m9.4 1v-7.1h2.7c.5 0 1 .1 1.4.3s.7.5.8.8.3.7.3 1.2-.1.8-.3 1.2c-.2.3-.5.6-.9.8s-.8.3-1.4.3h-1.9v-1.1h1.7c.3 0 .6 0 .8-.1s.4-.2.5-.4.1-.4.1-.6c0-.3 0-.5-.1-.7s-.3-.3-.5-.4-.5-.1-.8-.1h-1.2v6.1h-1.2zm4 0-1.7-3.2h1.4l1.8 3.2zm6.9-6v-1.1h5.7v1.1h-2.2v6.1h-1.3v-6.1zm10.4 6 2.5-7.1h1.6l2.5 7.1h-1.4l-1.3-3.7c-.1-.4-.3-.8-.4-1.3s-.3-1-.4-1.6h.3c-.2.6-.3 1.2-.4 1.7s-.3.9-.4 1.3l-1.2 3.7h-1.4zm1.5-1.7v-1h3.8v1zm11 1.7v-7.1h1.3v6.1h3.1v1.1H145z" class="st3"/> <path d="M6.48 45.77Q5.41 45.77 4.62 45.44Q3.83 45.1 3.38 44.47Q2.94 43.83 2.9 42.92H4.53Q4.57 43.4 4.83 43.72Q5.09 44.04 5.52 44.2Q5.95 44.36 6.47 44.36Q7.01 44.36 7.43 44.19Q7.85 44.03 8.08 43.72Q8.32 43.42 8.32 43.03Q8.32 42.67 8.11 42.43Q7.9 42.2 7.53 42.03Q7.15 41.87 6.65 41.75L5.6 41.47Q4.45 41.18 3.8 40.57Q3.15 39.97 3.15 38.99Q3.15 38.18 3.59 37.57Q4.03 36.95 4.8 36.61Q5.56 36.28 6.52 36.28Q7.51 36.28 8.25 36.62Q8.99 36.96 9.41 37.56Q9.83 38.16 9.85 38.93H8.25Q8.18 38.33 7.71 38.01Q7.25 37.68 6.5 37.68Q5.98 37.68 5.6 37.84Q5.23 38 5.03 38.27Q4.83 38.55 4.83 38.9Q4.83 39.28 5.06 39.53Q5.3 39.78 5.66 39.93Q6.03 40.08 6.4 40.17L7.26 40.39Q7.77 40.51 8.26 40.72Q8.74 40.92 9.13 41.24Q9.52 41.55 9.75 41.99Q9.97 42.44 9.97 43.04Q9.97 43.85 9.56 44.47Q9.15 45.08 8.37 45.43Q7.58 45.77 6.48 45.77ZM18.24 45.75Q17.03 45.75 16.08 45.19Q15.12 44.63 14.57 43.57Q14.02 42.51 14.02 41.02Q14.02 39.53 14.58 38.47Q15.13 37.4 16.08 36.84Q17.04 36.28 18.24 36.28Q19 36.28 19.66 36.49Q20.32 36.71 20.84 37.12Q21.35 37.53 21.69 38.12Q22.02 38.72 22.13 39.47H20.47Q20.39 39.06 20.19 38.74Q20 38.43 19.71 38.21Q19.41 37.98 19.05 37.87Q18.68 37.76 18.27 37.76Q17.51 37.76 16.93 38.14Q16.34 38.52 16.01 39.25Q15.68 39.97 15.68 41.02Q15.68 42.08 16.02 42.8Q16.35 43.53 16.93 43.9Q17.52 44.27 18.27 44.27Q18.68 44.27 19.04 44.16Q19.41 44.04 19.7 43.82Q20 43.6 20.19 43.28Q20.39 42.97 20.47 42.56H22.14Q22.05 43.22 21.73 43.8Q21.42 44.38 20.91 44.82Q20.41 45.26 19.74 45.51Q19.06 45.75 18.24 45.75ZM28.13 36.4V45.63H26.48V36.4ZM32.81 45.63V36.4H38.81V37.79H34.46V40.28H38.49V41.66H34.46V44.24H38.83V45.63ZM43.31 45.63V36.4H45.18L48.38 41.48Q48.57 41.8 48.8 42.2Q49.02 42.6 49.26 43.09Q49.5 43.58 49.74 44.19H49.55Q49.51 43.66 49.48 43.12Q49.44 42.57 49.43 42.07Q49.41 41.58 49.41 41.24V36.4H51.09V45.63H49.21L46.33 41.05Q46.06 40.61 45.82 40.19Q45.57 39.76 45.3 39.22Q45.02 38.68 44.62 37.92H44.85Q44.89 38.59 44.92 39.2Q44.96 39.8 44.98 40.28Q45 40.75 45 41.05V45.63ZM59.7 45.75Q58.49 45.75 57.53 45.19Q56.58 44.63 56.03 43.57Q55.48 42.51 55.48 41.02Q55.48 39.53 56.03 38.47Q56.58 37.4 57.54 36.84Q58.5 36.28 59.7 36.28Q60.46 36.28 61.12 36.49Q61.78 36.71 62.29 37.12Q62.81 37.53 63.14 38.12Q63.48 38.72 63.59 39.47H61.92Q61.84 39.06 61.65 38.74Q61.45 38.43 61.16 38.21Q60.87 37.98 60.51 37.87Q60.14 37.76 59.73 37.76Q58.97 37.76 58.38 38.14Q57.8 38.52 57.47 39.25Q57.14 39.97 57.14 41.02Q57.14 42.08 57.47 42.8Q57.81 43.53 58.39 43.9Q58.97 44.27 59.72 44.27Q60.14 44.27 60.5 44.16Q60.86 44.04 61.16 43.82Q61.45 43.6 61.65 43.28Q61.85 42.97 61.93 42.56H63.59Q63.5 43.22 63.19 43.8Q62.88 44.38 62.37 44.82Q61.86 45.26 61.19 45.51Q60.52 45.75 59.7 45.75ZM67.93 45.63V36.4H73.93V37.79H69.59V40.28H73.61V41.66H69.59V44.24H73.95V45.63ZM84.45 45.63V36.4H87.91Q88.97 36.4 89.69 36.8Q90.41 37.2 90.78 37.89Q91.15 38.57 91.15 39.46Q91.15 40.34 90.78 41.03Q90.41 41.72 89.68 42.11Q88.95 42.51 87.88 42.51H85.59V41.15H87.66Q88.29 41.15 88.69 40.93Q89.09 40.71 89.28 40.33Q89.46 39.95 89.46 39.46Q89.46 38.96 89.28 38.59Q89.09 38.21 88.69 38Q88.28 37.78 87.65 37.78H86.11V45.63ZM99.41 45.75Q98.2 45.75 97.24 45.19Q96.28 44.63 95.72 43.57Q95.17 42.51 95.17 41.02Q95.17 39.53 95.72 38.47Q96.28 37.4 97.24 36.84Q98.2 36.28 99.41 36.28Q100.62 36.28 101.57 36.84Q102.53 37.4 103.08 38.47Q103.64 39.53 103.64 41.02Q103.64 42.51 103.08 43.57Q102.53 44.63 101.57 45.19Q100.62 45.75 99.41 45.75ZM99.41 44.27Q100.16 44.27 100.74 43.9Q101.32 43.52 101.65 42.8Q101.98 42.07 101.98 41.02Q101.98 39.96 101.65 39.23Q101.32 38.51 100.74 38.13Q100.16 37.76 99.41 37.76Q98.65 37.76 98.07 38.13Q97.49 38.51 97.16 39.24Q96.83 39.97 96.83 41.02Q96.83 42.07 97.16 42.8Q97.49 43.52 98.07 43.9Q98.65 44.27 99.41 44.27ZM108.03 45.63V36.4H111.48Q112.54 36.4 113.26 36.77Q113.99 37.14 114.36 37.81Q114.73 38.47 114.73 39.35Q114.73 40.24 114.35 40.89Q113.98 41.54 113.25 41.89Q112.52 42.24 111.45 42.24H108.99V40.87H111.23Q111.86 40.87 112.26 40.69Q112.66 40.52 112.85 40.18Q113.04 39.84 113.04 39.35Q113.04 38.86 112.85 38.51Q112.66 38.16 112.26 37.97Q111.86 37.78 111.23 37.78H109.68V45.63ZM113.22 45.63 110.98 41.44H112.79L115.07 45.63ZM118.71 37.79V36.4H126.06V37.79H123.21V45.63H121.56V37.79ZM129.7 45.63 132.92 36.4H135.02L138.31 45.63H136.5L134.86 40.84Q134.62 40.09 134.36 39.15Q134.1 38.22 133.78 37.02H134.13Q133.82 38.23 133.57 39.17Q133.32 40.12 133.09 40.84L131.51 45.63ZM131.58 43.34V42.01H136.44V43.34ZM142.37 45.63V36.4H144.02V44.24H148.1V45.63Z" class="st3"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -1,12 +0,0 @@
---
title: "Consectetur adipiscing"
introduction: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
---
## Consectetur adipiscing
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
- Lorem ipsum dolor sit amet.
- Consectetur adipiscing elit.
- Sed do eiusmod tempor incididunt.
-19
View File
@@ -1,19 +0,0 @@
---
title: "Dolor sit amet"
introduction: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
image: "/uploads/hero.jpg"
imageAlt: "Telescope beneath a star-filled night sky"
imageCaption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
---
## Dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam.
Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti.
Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.
-25
View File
@@ -1,25 +0,0 @@
---
title: "Lorem Ipsum Dolor Sit Amet Consectetur"
introduction: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
image: "/uploads/hero.jpg"
imageAlt: "Telescope beneath a star-filled night sky"
imageCaption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
buttonLabel: "Lorem ipsum"
buttonUrl: "/example/dolor-sit-amet/"
---
## Lorem Ipsum Dolor
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat ([lorem ipsum](/example/dolor-sit-amet/)). Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo [inventore veritatis](/example/consectetur-adipiscing/) et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia [consequuntur magni](/example/sed-do-eiusmod/) dolores eos qui ratione voluptatem sequi nesciunt.
> Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
## Consectetur Adipiscing Elit
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam.
Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti.
Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.
-16
View File
@@ -1,16 +0,0 @@
---
title: "Sed do eiusmod"
introduction: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
---
## Sed do eiusmod
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam.
Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti.
Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

+43 -27
View File
@@ -1,34 +1,50 @@
// Copy the four editable files into Astro's ignored content directory. // Point the site at a checkout of the ctao/content repository (default: the
// --ensure seeds only a fresh checkout; an incomplete existing set fails loudly. // sibling ../content clone), the same content the live demo builds from.
import { cpSync, existsSync, mkdirSync, readFileSync } from 'node:fs'; // src/content/pages and public/uploads become links into that checkout, so
import { resolve, join } from 'node:path'; // saves from Sveltia's local-repository mode show up in `npm run dev` directly.
//
// node scripts/content.mjs link if missing, then check the pages (pre-hooks)
// npm run content:link -- [dir] replace existing links or copies
//
// The deploy build copies the content into place instead (deploy/build.sh);
// existing directories are left alone unless --relink is given.
import { existsSync, lstatSync, mkdirSync, readFileSync, rmSync, symlinkSync, unlinkSync } from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
const root = fileURLToPath(new URL('../', import.meta.url)); const root = fileURLToPath(new URL('../', import.meta.url));
const examples = JSON.parse(readFileSync(join(root, 'src/data/examples.json'), 'utf8')); const examples = JSON.parse(readFileSync(join(root, 'src/data/examples.json'), 'utf8'));
const ensure = process.argv.includes('--ensure'); const relink = process.argv.includes('--relink');
const source = resolve(process.argv[2] && !ensure ? process.argv[2] : join(root, 'sample-content')); const source = resolve(root, process.argv.slice(2).find((arg) => !arg.startsWith('--')) ?? '../content');
const targets = [
{ from: join(source, 'pages'), to: join(root, 'src/content/pages') },
{ from: join(source, 'uploads'), to: join(root, 'public/uploads') },
];
const exists = (path) => {
try { lstatSync(path); return true; } catch { return false; }
};
for (const { from, to } of targets) {
if (exists(to) && !relink) continue;
if (!existsSync(from)) {
throw new Error(`Missing ${from}. Clone the content repository first:\n` +
` git clone https://astro-git.isl-dev.grid.cyfronet.pl/ctao/content.git ${relative(root, source) || '.'}\n` +
'or pass another checkout: npm run content:link -- <path>');
}
if (exists(to)) {
// Remove only the link itself, never the content it points to.
if (lstatSync(to).isSymbolicLink()) unlinkSync(to);
else rmSync(to, { recursive: true }); // an old copied overlay
}
mkdirSync(dirname(to), { recursive: true });
symlinkSync(from, to, 'junction'); // a junction needs no admin rights on Windows
console.log(`Linked ${relative(root, to)} -> ${from}`);
}
const pages = join(root, 'src/content/pages'); const pages = join(root, 'src/content/pages');
const uploads = join(root, 'public/uploads'); for (const slug of ['home', ...examples.map(({ slug }) => slug)]) {
if (!existsSync(join(pages, `${slug}.md`))) {
function validate(directory) { throw new Error(`Missing ${join(pages, `${slug}.md`)}. Check the content checkout, or run npm run content:link -- <path>.`);
for (const { slug } of examples) {
if (!existsSync(join(directory, `${slug}.md`))) {
throw new Error(`Missing ${join(directory, `${slug}.md`)}. Run npm run content:setup or npm run content:sync -- <content-clone>.`);
}
} }
} }
if (ensure && existsSync(pages)) {
validate(pages);
} else {
validate(join(source, 'pages'));
if (!existsSync(join(source, 'uploads'))) throw new Error(`Missing uploads directory in ${source}`);
mkdirSync(pages, { recursive: true });
mkdirSync(uploads, { recursive: true });
for (const { slug } of examples) {
cpSync(join(source, 'pages', `${slug}.md`), join(pages, `${slug}.md`));
}
cpSync(join(source, 'uploads'), uploads, { recursive: true });
console.log(`Example content copied from ${source}`);
}
-7
View File
@@ -1,7 +0,0 @@
#!/usr/bin/env bash
# Local dev: put the content repo's files where the build expects them.
# Usage: ./scripts/link-content.sh [path-to-content-clone] (default ../ctao-content)
# Cross-platform implementation is shared with npm run content:sync.
set -euo pipefail
cd "$(dirname "$0")/.."
node scripts/content.mjs "${1:-../ctao-content}"
-19
View File
@@ -1,19 +0,0 @@
// Prepare a disposable content repository for Sveltia's local folder picker.
import { cpSync, existsSync, mkdirSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { join } from 'node:path';
const root = fileURLToPath(new URL('../', import.meta.url));
const local = join(root, '.tmp/content');
if (!existsSync(local)) {
mkdirSync(local, { recursive: true });
cpSync(join(root, 'sample-content/pages'), join(local, 'pages'), { recursive: true });
cpSync(join(root, 'sample-content/uploads'), join(local, 'uploads'), { recursive: true });
}
if (!existsSync(join(local, '.git'))) {
execFileSync('git', ['init', '--quiet', '--initial-branch=main', local], { stdio: 'inherit' });
}
execFileSync(process.execPath, [join(root, 'scripts/content.mjs'), local], { stdio: 'inherit' });
console.log(`In Sveltia, choose Work with Local Repository and select: ${local}`);
console.log('After saving, run: npm run content:sync -- .tmp/content');
+24 -10
View File
@@ -1,13 +1,27 @@
// Derive editor body styles from the mockup CSS so the preview cannot drift. // Derive editor preview styles from the site CSS so the preview cannot drift.
// The preview template in src/pages/admin/index.astro renders the same markup
// as the Example pages, so the page hero, figure and prose rules apply as-is.
import { readFileSync, writeFileSync } from 'node:fs'; import { readFileSync, writeFileSync } from 'node:fs';
const css = readFileSync(new URL('../src/styles/global.css', import.meta.url), 'utf8'); const css = readFileSync(new URL('../src/styles/global.css', import.meta.url), 'utf8').replace(/\r\n/g, '\n');
const base = css.slice(0, css.indexOf('/* Utilities')); const between = (start, end) => {
const prose = css.slice(css.indexOf('/* Prose'), css.indexOf('/* Footer')); const from = css.indexOf(start);
const markdown = css.slice(css.indexOf('.prose { overflow-wrap:'), css.indexOf('.submenu a[aria-current="page"]')); const to = end ? css.indexOf(end, from) : css.length;
const preview = (base + prose + markdown).replace(/\.prose\b/g, 'body'); if (from < 0 || to < 0) throw new Error(`preview-styles: marker not found in global.css: ${from < 0 ? start : end}`);
return css.slice(from, to).trim();
};
const preview = [
between('/* ===', '/* Header'), // fonts, tokens, base, utilities
between('/* Page hero', '/* Footer'), // hero, figure, prose, buttons
between('/* Preferences'), // high contrast, reduced motion
`/* Preview frame
========================================================================== */
/* There is no site header above the hero */
.page-hero {
margin-top: 0;
padding-top: 3rem;
}`,
];
writeFileSync(new URL('../public/admin/preview.css', import.meta.url), writeFileSync(new URL('../public/admin/preview.css', import.meta.url),
'/* Generated by scripts/preview-styles.mjs; edit src/styles/global.css. */\n' + preview + ` '/* Generated by scripts/preview-styles.mjs; edit src/styles/global.css. */\n\n' + preview.join('\n\n') + '\n');
body { display: block; min-height: 0; max-width: 64rem; margin: 0 auto; padding: 1.5rem; }
h1 { color: var(--galaxy-blue); font-family: var(--font-head); font-weight: 500; line-height: 1.05; }
`);
+4 -4
View File
@@ -1,16 +1,16 @@
--- ---
interface Props { title: string; introduction?: string; example?: boolean; hasImage?: boolean } interface Props { title: string; introduction?: string; example?: boolean; breadcrumb?: boolean; hasImage?: boolean }
const { title, introduction, example = false, hasImage = false } = Astro.props; const { title, introduction, example = false, breadcrumb = true, hasImage = false } = Astro.props;
--- ---
<section class:list={['page-hero', { 'page-hero--compact': !hasImage }]}> <section class:list={['page-hero', { 'page-hero--compact': !hasImage }]}>
<div class="container"> <div class="container">
<nav class="breadcrumb" aria-label="Breadcrumb"> {breadcrumb && <nav class="breadcrumb" aria-label="Breadcrumb">
<ol> <ol>
{Astro.url.pathname !== '/' && <li><a href="/">Home</a></li>} {Astro.url.pathname !== '/' && <li><a href="/">Home</a></li>}
{example && <li>Example</li>} {example && <li>Example</li>}
<li aria-current="page">{title}</li> <li aria-current="page">{title}</li>
</ol> </ol>
</nav> </nav>}
<h1 class="page-hero__title">{title}</h1> <h1 class="page-hero__title">{title}</h1>
{introduction && <p class="page-hero__lead">{introduction}</p>} {introduction && <p class="page-hero__lead">{introduction}</p>}
</div> </div>
+9 -2
View File
@@ -8,17 +8,24 @@ const destination = z.string().refine(
'Use a site path, https:// URL, mailto: address, or fragment.', 'Use a site path, https:// URL, mailto: address, or fragment.',
); );
const pages = defineCollection({ const pages = defineCollection({
// Only these four files can become public content pages. // Only the Home page and the four Example files can become public pages.
loader: glob({ loader: glob({
pattern: `{${examples.map(({ slug }) => slug).join(',')}}.md`, pattern: `{home,${examples.map(({ slug }) => slug).join(',')}}.md`,
base: './src/content/pages', base: './src/content/pages',
}), }),
schema: z.object({ schema: z.object({
title: z.string().min(1), title: z.string().min(1),
introduction: z.string().optional(), introduction: z.string().optional(),
// Example pages: one photo
image: z.string().optional(), image: z.string().optional(),
imageAlt: z.string().optional(), imageAlt: z.string().optional(),
imageCaption: z.string().optional(), imageCaption: z.string().optional(),
// Home: up to two photos side by side
images: z.array(z.object({
image: z.string().min(1),
alt: z.string().optional(),
caption: z.string().optional(),
})).max(2).optional(),
buttonLabel: z.string().optional(), buttonLabel: z.string().optional(),
buttonUrl: destination.optional(), buttonUrl: destination.optional(),
}), }),
+2 -2
View File
@@ -24,7 +24,7 @@ const canonical = new URL(Astro.url.pathname, Astro.site);
<div class="site-header__inner"> <div class="site-header__inner">
<a class="site-header__logo" href="/"> <a class="site-header__logo" href="/">
<img src="/assets/logo-sp-negative.svg" alt="CTAO – Home" width="153" height="43.9"> <img src="/assets/logo-sp-negative.svg" alt="CTAO – Home" width="153" height="46">
</a> </a>
<nav class="site-nav" id="site-nav" aria-label="Main"> <nav class="site-nav" id="site-nav" aria-label="Main">
@@ -104,7 +104,7 @@ const canonical = new URL(Astro.url.pathname, Astro.site);
<main id="main" tabindex="-1"><slot /></main> <main id="main" tabindex="-1"><slot /></main>
<footer class="site-footer"> <footer class="site-footer">
<div class="container site-footer__inner"> <div class="container site-footer__inner">
<img class="site-footer__logo" src="/assets/logo-sp-negative.svg" alt="CTAO" width="153" height="43.9" /> <img class="site-footer__logo" src="/assets/logo-sp-negative.svg" alt="CTAO" width="153" height="46" />
<ul class="site-footer__nav"> <ul class="site-footer__nav">
<li><a href="/pages/privacy/">Privacy</a></li> <li><a href="/pages/privacy/">Privacy</a></li>
<li><a href="/pages/disclaimer/">Disclaimer</a></li> <li><a href="/pages/disclaimer/">Disclaimer</a></li>
+36
View File
@@ -0,0 +1,36 @@
---
// Editable content page: hero, one photo (Example pages) or two side by side
// (Home), Markdown body and optional button. The CMS preview template in
// src/pages/admin/index.astro mirrors this markup.
import type { CollectionEntry } from 'astro:content';
import { render } from 'astro:content';
import Base from './Base.astro';
import PageHero from '../components/PageHero.astro';
interface Props { page: CollectionEntry<'pages'>; example?: boolean; breadcrumb?: boolean }
const { page, example = false, breadcrumb = true } = Astro.props;
const { Content } = await render(page);
const { title, introduction, image, imageAlt, imageCaption, images, buttonLabel, buttonUrl } = page.data;
const photos = images ?? (image ? [{ image, alt: imageAlt, caption: imageCaption }] : []);
---
<Base title={example ? title : undefined} description={introduction}>
<PageHero title={title} introduction={introduction} example={example} breadcrumb={breadcrumb} hasImage={photos.length > 0} />
{photos.length > 0 && <div class="container">
<div class:list={{ 'page-figures': photos.length > 1 }}>
{photos.map(({ image, alt, caption }) => (
<figure class="page-figure">
<img src={image} alt={alt || ''} />
{caption && <figcaption>{caption}</figcaption>}
</figure>
))}
</div>
</div>}
<article class:list={['container', 'prose', { 'prose--no-image': photos.length === 0 }]}>
<Content />
{buttonLabel && buttonUrl && <p>
<a class="btn btn--primary" href={buttonUrl}>
<span class="btn__label">{buttonLabel}</span><span class="arrow" aria-hidden="true">→</span>
</a>
</p>}
</article>
</Base>
+62
View File
@@ -5,6 +5,9 @@
// deliberate — see README); note the bundle still fetches its own UI fonts // deliberate — see README); note the bundle still fetches its own UI fonts
// from jsDelivr at runtime. Kept is:inline so Astro doesn't process/bundle it. // from jsDelivr at runtime. Kept is:inline so Astro doesn't process/bundle it.
// CTAO branding on the sign-in screen comes from the documented `logo` option. // CTAO branding on the sign-in screen comes from the documented `logo` option.
import examples from '../../data/examples.json';
const previewNames = examples.map(({ slug }) => slug);
--- ---
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
@@ -32,5 +35,64 @@
// Generated from the site's CSS; see scripts/preview-styles.mjs. // Generated from the site's CSS; see scripts/preview-styles.mjs.
window.CMS?.registerPreviewStyle?.('/admin/preview.css'); window.CMS?.registerPreviewStyle?.('/admin/preview.css');
</script> </script>
<script is:inline define:vars={{ previewNames }}>
// Preview pane: the same markup as src/layouts/ContentPage.astro and
// PageHero.astro, without field labels. Sveltia looks templates up by
// file name for file collections, so register one per page.
const { h, rf } = window;
// Our own <img>: Sveltia's image widget keeps the image transparent until
// a visibility check that does not complete inside a custom template.
// The asset URL starts as the public path and becomes a blob URL once
// Sveltia has fetched the file, which covers images not deployed yet.
const PreviewImage = ({ asset, fallback, alt }) => h('img', {
src: asset ? String(asset) : fallback,
alt,
onError: ({ currentTarget: img }) => {
const retry = (tries) => {
const url = asset && String(asset);
if (url && url !== img.getAttribute('src')) img.src = url;
else if (tries) setTimeout(() => retry(tries - 1), 500);
};
retry(10);
},
});
const PagePreview = ({ entry, widgetFor, getAsset, home = false }) => {
const get = (key) => entry.getIn(['data', key]);
const title = get('title') || '';
const introduction = get('introduction');
const buttonLabel = get('buttonLabel');
const buttonUrl = get('buttonUrl');
// Home: a list of up to two photos; Example pages: a single photo
const photos = home
? (get('images')?.toJS?.() ?? []).filter((item) => item?.image)
: get('image') ? [{ image: get('image'), alt: get('imageAlt'), caption: get('imageCaption') }] : [];
return h(rf, null,
h('section', { className: photos.length ? 'page-hero' : 'page-hero page-hero--compact' },
h('div', { className: 'container' },
!home && h('nav', { className: 'breadcrumb', 'aria-label': 'Breadcrumb' },
h('ol', null,
h('li', null, h('a', { href: '/' }, 'Home')),
h('li', null, 'Example'),
h('li', { 'aria-current': 'page' }, title))),
h('h1', { className: 'page-hero__title' }, title),
introduction && h('p', { className: 'page-hero__lead' }, introduction))),
photos.length > 0 && h('div', { className: 'container' },
h('div', { className: photos.length > 1 ? 'page-figures' : undefined },
photos.map(({ image, alt, caption }, index) => h('figure', { className: 'page-figure', key: index },
h(PreviewImage, { key: image, asset: getAsset(image), fallback: image, alt: alt || '' }),
caption && h('figcaption', null, caption))))),
h('article', { className: photos.length ? 'container prose' : 'container prose prose--no-image' },
widgetFor('body'),
buttonLabel && buttonUrl && h('p', null,
h('a', { className: 'btn btn--primary', href: buttonUrl },
h('span', { className: 'btn__label' }, buttonLabel),
h('span', { className: 'arrow', 'aria-hidden': 'true' }, '→')))));
};
const HomePreview = (props) => h(PagePreview, { ...props, home: true });
if (window.CMS?.registerPreviewTemplate && h) {
window.CMS.registerPreviewTemplate('home', HomePreview);
for (const name of previewNames) window.CMS.registerPreviewTemplate(name, PagePreview);
}
</script>
</body> </body>
</html> </html>
+4 -23
View File
@@ -1,35 +1,16 @@
--- ---
import { getCollection, render } from 'astro:content'; import { getCollection } from 'astro:content';
import Base from '../../layouts/Base.astro'; import ContentPage from '../../layouts/ContentPage.astro';
import PageHero from '../../components/PageHero.astro';
import examples from '../../data/examples.json'; import examples from '../../data/examples.json';
export async function getStaticPaths() { export async function getStaticPaths() {
const pages = await getCollection('pages'); const pages = await getCollection('pages');
return examples.map(({ slug }) => { return examples.map(({ slug }) => {
const page = pages.find(({ id }) => id === slug); const page = pages.find(({ id }) => id === slug);
if (!page) throw new Error(`Missing Example page: ${slug}. Run npm run content:setup.`); if (!page) throw new Error(`Missing Example page: ${slug}. Check the content checkout (npm run content:link -- <path>).`);
return { params: { slug }, props: { page } }; return { params: { slug }, props: { page } };
}); });
} }
const { page } = Astro.props; const { page } = Astro.props;
const { Content } = await render(page);
const { title, introduction, image, imageAlt, imageCaption, buttonLabel, buttonUrl } = page.data;
--- ---
<Base title={title} description={introduction}> <ContentPage page={page} example />
<PageHero title={title} introduction={introduction} example hasImage={Boolean(image)} />
{image && <div class="container">
<figure class="page-figure">
<img src={image} alt={imageAlt || ''} />
{imageCaption && <figcaption>{imageCaption}</figcaption>}
</figure>
</div>}
<article class:list={['container', 'prose', { 'prose--no-image': !image }]}>
<Content />
{buttonLabel && buttonUrl && <p>
<a class="btn btn--primary" href={buttonUrl}>
<span class="btn__label">{buttonLabel}</span><span class="arrow" aria-hidden="true">→</span>
</a>
</p>}
</article>
</Base>
+6 -2
View File
@@ -1,4 +1,8 @@
--- ---
import Placeholder from '../layouts/Placeholder.astro'; import { getEntry } from 'astro:content';
import ContentPage from '../layouts/ContentPage.astro';
const page = await getEntry('pages', 'home');
if (!page) throw new Error('Missing Home page: pages/home.md. Check the content checkout (npm run content:link -- <path>).');
--- ---
<Placeholder title="CTAO Science Portal" /> <ContentPage page={page} breadcrumb={false} />
+136 -83
View File
@@ -1,6 +1,8 @@
/* ========================================================================== /* ==========================================================================
CTAO Science Portal: content page mockup CTAO Science Portal: content page mockup
Colours and type follow the CTAO Brand Guidelines (April 2024). Colours and type follow the CTAO Brand Guidelines (April 2024).
The editor preview (public/admin/preview.css) is generated from the
hero, prose and button sections below by scripts/preview-styles.mjs.
========================================================================== */ ========================================================================== */
/* Fonts /* Fonts
@@ -49,9 +51,10 @@
--gutter: clamp(1rem, 4vw, 2.5rem); --gutter: clamp(1rem, 4vw, 2.5rem);
--header-height: 7rem; --header-height: 7rem;
--header-height-scrolled: 5rem; --header-height-scrolled: 5rem;
--radius-lg: 60px; --radius-lg: min(15vw, 180px);
--radius-md: 30px;
--radius-sm: 15px; --radius-sm: 15px;
/* How far photos are pulled up into the hero */
--hero-overlap: clamp(6rem, 11.25vw, 9rem);
/* Dropdown submenus (Example, language) */ /* Dropdown submenus (Example, language) */
--radius-menu: 8px; --radius-menu: 8px;
--transition: 0.3s ease-in-out; --transition: 0.3s ease-in-out;
@@ -194,17 +197,17 @@ button:hover .arrow {
transition: background-color var(--transition); transition: background-color var(--transition);
} }
/* Solid once the page scrolls or a full-width panel is open */
.site-header.is-scrolled, .site-header.is-scrolled,
.site-header.is-open { .site-header.is-open {
background: var(--galaxy-blue); background: var(--galaxy-blue);
transition: none;
} }
.site-header__inner { .site-header__inner {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 1.5rem; gap: clamp(0.5rem, 4vw, 1.5rem);
width: min(100% - 2 * var(--gutter), var(--content-width)); width: min(100% - 2 * var(--gutter), var(--content-width));
height: var(--header-height); height: var(--header-height);
margin-inline: auto; margin-inline: auto;
@@ -221,8 +224,8 @@ button:hover .arrow {
} }
.site-header__logo img { .site-header__logo img {
width: 153px; width: clamp(120px, 32vw, 153px);
height: 44px; height: auto;
} }
/* Submenus: one disclosure dropdown shared by Example and the language menu /* Submenus: one disclosure dropdown shared by Example and the language menu
@@ -284,6 +287,19 @@ button:hover .arrow {
color: var(--cherenkov-blue); color: var(--cherenkov-blue);
} }
.submenu a[aria-current="page"] {
color: var(--cherenkov-blue);
}
/* Languages without a translation yet */
.submenu__unavailable {
display: block;
padding: 0.5rem 0.75rem;
font-size: 0.9375rem;
color: #b3b3c9;
cursor: default;
}
/* Main nav /* Main nav
========================================================================== */ ========================================================================== */
@@ -321,6 +337,10 @@ button:hover .arrow {
text-decoration: none; text-decoration: none;
} }
.site-nav__link[aria-current="page"] {
color: var(--cherenkov-blue);
}
.site-nav__label { .site-nav__label {
position: relative; position: relative;
} }
@@ -361,7 +381,7 @@ button:hover .arrow {
display: flex; display: flex;
flex: 0 0 auto; flex: 0 0 auto;
align-items: center; align-items: center;
gap: 0.625rem; gap: clamp(0.25rem, 1.5vw, 0.625rem);
} }
.pill { .pill {
@@ -445,6 +465,7 @@ button:hover .arrow {
.search-panel__input { .search-panel__input {
flex: 1; flex: 1;
min-width: 0;
padding: 0.75rem 0; padding: 0.75rem 0;
font: inherit; font: inherit;
font-size: clamp(1.25rem, 3vw, 2rem); font-size: clamp(1.25rem, 3vw, 2rem);
@@ -454,6 +475,11 @@ button:hover .arrow {
outline: none; outline: none;
} }
.search-panel__input:focus-visible {
outline: 2px solid var(--cherenkov-blue);
outline-offset: 4px;
}
.search-panel__input::placeholder { .search-panel__input::placeholder {
color: rgba(255, 255, 255, 0.5); color: rgba(255, 255, 255, 0.5);
} }
@@ -478,7 +504,7 @@ button:hover .arrow {
} }
.site-header:not(.is-open) { .site-header:not(.is-open) {
border-bottom-right-radius: var(--radius-md); border-bottom-right-radius: var(--radius-lg);
} }
.site-header__signin { .site-header__signin {
@@ -555,43 +581,22 @@ button:hover .arrow {
} }
} }
@media (max-width: 480px) { /* Page hero
.site-header__logo img { ========================================================================== */
width: 120px;
height: auto;
}
}
@media (max-width: 359px) {
.site-header__inner {
gap: 0.5rem;
}
.site-header__tools {
gap: 0.25rem;
}
}
/* Page hero ========================================================================== */
.page-hero { .page-hero {
--hero-veil: linear-gradient(
90deg,
rgba(0, 0, 74, 0.72) 0%,
rgba(0, 0, 74, 0.45) 45%,
rgba(0, 0, 74, 0) 75%
);
margin-top: calc(-1 * var(--header-height)); margin-top: calc(-1 * var(--header-height));
padding: calc(var(--header-height) + 3rem) 0 12rem; padding: calc(var(--header-height) + 3rem) 0 calc(var(--hero-overlap) + 3rem);
color: var(--white); color: var(--white);
background: background: var(--grain), var(--galaxy-blue);
var(--grain),
linear-gradient(180deg, rgba(0, 0, 74, 0.55) 0, rgba(0, 0, 74, 0) 10rem),
var(--hero-veil),
#00004a;
border-bottom-right-radius: var(--radius-lg); border-bottom-right-radius: var(--radius-lg);
} }
/* Pages without a photo: no room needed for the figure to overlap */
.page-hero--compact {
padding-bottom: 4rem;
}
.breadcrumb ol { .breadcrumb ol {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -631,6 +636,7 @@ button:hover .arrow {
line-height: 1.05; line-height: 1.05;
letter-spacing: -0.02em; letter-spacing: -0.02em;
text-wrap: balance; text-wrap: balance;
overflow-wrap: anywhere;
} }
.page-hero__lead { .page-hero__lead {
@@ -643,7 +649,7 @@ button:hover .arrow {
/* Photo pulled up over the hero; flat, no shadow */ /* Photo pulled up over the hero; flat, no shadow */
.page-figure { .page-figure {
margin: -9rem 0 4rem; margin: calc(-1 * var(--hero-overlap)) 0 4rem;
} }
.page-figure img { .page-figure img {
@@ -657,11 +663,35 @@ button:hover .arrow {
color: var(--text-muted); color: var(--text-muted);
} }
/* Two photos side by side (Home), pulled up over the hero together; they
stack by themselves once each would be narrower than 18rem */
.page-figures {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: 1.5rem;
margin: calc(-1 * var(--hero-overlap)) 0 4rem;
}
.page-figures .page-figure {
margin: 0;
}
/* Same shape for both photos, whatever the uploads' proportions */
.page-figures img {
aspect-ratio: 3 / 2;
object-fit: cover;
}
/* Prose /* Prose
========================================================================== */ ========================================================================== */
.prose { .prose {
padding-bottom: 5rem; padding-bottom: 5rem;
overflow-wrap: anywhere;
}
.prose--no-image {
padding-top: 4rem;
} }
.prose > * { .prose > * {
@@ -708,6 +738,61 @@ button:hover .arrow {
border-left: 4px solid var(--cherenkov-blue); border-left: 4px solid var(--cherenkov-blue);
} }
.prose blockquote p {
font-size: inherit;
line-height: inherit;
}
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
font-family: var(--font-head);
color: var(--galaxy-blue);
}
.prose li {
margin-bottom: 0.5rem;
}
.prose img {
border-radius: var(--radius-sm);
}
.prose pre {
padding: 1rem;
overflow-x: auto;
background: var(--moon-gray);
}
.prose code {
overflow-wrap: normal;
}
.prose table {
display: block;
overflow-x: auto;
border-collapse: collapse;
}
.prose th,
.prose td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #ddd;
}
.prose hr {
margin: 2rem 0;
border: 0;
border-top: 1px solid #ddd;
}
/* Placeholder pages: empty body until their content exists */
.placeholder-body {
min-height: 12rem;
}
/* Buttons /* Buttons
========================================================================== */ ========================================================================== */
@@ -751,6 +836,18 @@ button:hover .arrow {
/* Footer /* Footer
========================================================================== */ ========================================================================== */
/* Keep the footer at the bottom of short pages */
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1;
min-width: 0;
}
.site-footer { .site-footer {
margin-top: 2rem; margin-top: 2rem;
padding: 3rem 0; padding: 3rem 0;
@@ -791,27 +888,6 @@ button:hover .arrow {
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.6);
} }
/* Responsive page
========================================================================== */
@media (max-width: 800px) {
/* Text spans the full width here, so keep at least 50% navy everywhere */
.page-hero {
--hero-veil: linear-gradient(
90deg,
rgba(0, 0, 74, 0.72) 0%,
rgba(0, 0, 74, 0.5) 100%
);
padding-bottom: 8rem;
border-bottom-right-radius: var(--radius-md);
}
.page-figure {
margin-top: -6rem;
}
}
/* Preferences /* Preferences
========================================================================== */ ========================================================================== */
@@ -830,26 +906,3 @@ button:hover .arrow {
transition: none !important; transition: none !important;
} }
} }
/* Page variants and Markdown elements. Preview styles derive from this file. */
body { min-height: 100vh; display: flex; flex-direction: column; }
main { flex: 1; min-width: 0; }
.page-hero--compact { padding-bottom: 4rem; }
.placeholder-body { min-height: 12rem; }
.prose--no-image { padding-top: 4rem; }
.prose { overflow-wrap: anywhere; }
.prose h3, .prose h4, .prose h5, .prose h6 { color: var(--galaxy-blue); font-family: var(--font-head); }
.prose li { margin-bottom: 0.5rem; }
.prose blockquote p { font-size: inherit; line-height: inherit; }
.prose pre { padding: 1rem; overflow-x: auto; background: var(--moon-gray); }
.prose code { overflow-wrap: normal; }
.prose table { display: block; overflow-x: auto; border-collapse: collapse; }
.prose th, .prose td { padding: 0.75rem; border-bottom: 1px solid #ddd; text-align: left; }
.prose hr { margin: 2rem 0; border: 0; border-top: 1px solid #ddd; }
.prose img { border-radius: var(--radius-sm); }
.submenu a[aria-current="page"], .site-nav__link[aria-current="page"] { color: var(--cherenkov-blue); }
.submenu__unavailable { display: block; padding: 0.5rem 0.75rem; font-size: 0.9375rem; color: #b3b3c9; cursor: default; }
.search-panel__input { min-width: 0; }
.search-panel__input:focus-visible { outline: 2px solid var(--cherenkov-blue); outline-offset: 4px; }
.page-hero__title { overflow-wrap: anywhere; }