Compare commits
52 Commits
a94d765cca
..
v2.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| a946e66c6c | |||
| 601350c5c6 | |||
| 47393012f2 | |||
| cbd8b5e6fb | |||
| 2b16b19246 | |||
| d9d40c350d | |||
| 55e371f506 | |||
| eda5fba8f3 | |||
| f2e078b593 | |||
| 80af8df8b0 | |||
| 4e527b19d5 | |||
| 3e93efb785 | |||
| 02c36dba09 | |||
| 085cca2812 | |||
| 0a93340792 | |||
| 87cd070beb | |||
| 6004203339 | |||
| 278eda7d69 | |||
| abddc59f49 | |||
| 24e9aa408b | |||
| 2bdee5f215 | |||
| a6d14f9267 | |||
| bb0c490cc7 | |||
| 2ab3965640 | |||
| 061669a7cc | |||
| 5a7d7feace | |||
| 47eb475887 | |||
| c6d2792332 | |||
| ab07c94141 | |||
| df8a187af2 | |||
| ccbd27916c | |||
| 2b6b2c06c2 | |||
| 0ed3a8fe64 | |||
| 0683686fcb | |||
| 486438772d | |||
| 27e4c8243c | |||
| 829914a271 | |||
| af2a5c4912 | |||
| 6a64df96d0 | |||
| e5f0bf3fed | |||
| 25e916c3be | |||
| 1d7680330d | |||
| 9323fb69e8 | |||
| 07cf13efcd | |||
| 4c3eec7631 | |||
| 71225308d3 | |||
| a5958d47a4 | |||
| 99c61cf7e3 | |||
| 32f4c92f1b | |||
| 4c7a33a6fa | |||
| 9eb0bc1c3e | |||
| 0fb0eec7df |
@@ -1,10 +1,22 @@
|
|||||||
# Release — creates ZIP packages for Chrome, Firefox and Opera on new tag
|
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
|
# Wird bei einem vX.Y.Z-Tag-Push ausgeloest. Baut die drei Web-Extension-ZIPs
|
||||||
|
# (Chrome/Firefox/Opera) und haengt sie ans passende Gitea-Release.
|
||||||
|
#
|
||||||
|
# Portiert von GitHub Actions auf Gitea Actions (2026-06): der fruehere
|
||||||
|
# softprops/action-gh-release-Step ist GitHub-spezifisch und laeuft auf Gitea
|
||||||
|
# nicht. Ersetzt durch die Gitea-native release-action (volle gitea.com-URL,
|
||||||
|
# da DEFAULT_ACTIONS_URL=github nackte Namen sonst von github.com zieht).
|
||||||
|
# Muster uebernommen aus HellionChat/.gitea/workflows/release.yml.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
# Manueller Recovery-Trigger: in Gitea "Run workflow" und den Tag (z.B. v2.2.0)
|
||||||
|
# im Ref-Dropdown waehlen, NICHT master. Der Validate-Step unten failt hart
|
||||||
|
# bei einem Nicht-Tag-Ref, weil die release-action GITHUB_REF direkt liest.
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -13,7 +25,20 @@ jobs:
|
|||||||
build-release:
|
build-release:
|
||||||
name: Build & Release
|
name: Build & Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
# release-action liest GITHUB_REF direkt (kein tag_name-Input). Vorab
|
||||||
|
# validieren, damit manuelle Dispatches von einem Branch-Ref hier laut
|
||||||
|
# scheitern statt nach einem vollen Build.
|
||||||
|
- name: Validate tag ref
|
||||||
|
run: |
|
||||||
|
if [[ "${GITHUB_REF}" != refs/tags/v* ]]; then
|
||||||
|
echo "::error::Release-Workflow muss auf einem v*-Tag laufen, got ${GITHUB_REF}"
|
||||||
|
echo "::error::Tag pushen, oder im workflow_dispatch-Ref-Dropdown den Tag (nicht master) waehlen."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
@@ -26,7 +51,7 @@ jobs:
|
|||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip" \
|
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip" \
|
||||||
manifest.json newtab.html src/js/*.js src/css/ assets/ _locales/ \
|
manifest.json newtab.html src/js/*.js src/css/ assets/ _locales/ \
|
||||||
-x "*.git*" "dist/*" ".github/*" "src/js/opera/*"
|
-x "*.git*" "dist/*" ".github/*" ".gitea/*" "src/js/opera/*"
|
||||||
|
|
||||||
- name: Create Firefox ZIP (Manifest V3)
|
- name: Create Firefox ZIP (Manifest V3)
|
||||||
run: |
|
run: |
|
||||||
@@ -34,7 +59,7 @@ jobs:
|
|||||||
cp manifest.firefox.json manifest.json
|
cp manifest.firefox.json manifest.json
|
||||||
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip" \
|
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip" \
|
||||||
manifest.json newtab.html src/js/*.js src/css/ assets/ _locales/ \
|
manifest.json newtab.html src/js/*.js src/css/ assets/ _locales/ \
|
||||||
-x "*.git*" "dist/*" ".github/*" "manifest.chrome-backup.json" "manifest.firefox.json" "src/js/opera/*"
|
-x "*.git*" "dist/*" ".github/*" ".gitea/*" "manifest.chrome-backup.json" "manifest.firefox.json" "src/js/opera/*"
|
||||||
mv manifest.chrome-backup.json manifest.json
|
mv manifest.chrome-backup.json manifest.json
|
||||||
|
|
||||||
- name: Create Opera/Opera GX ZIP (Manifest V3 + workaround)
|
- name: Create Opera/Opera GX ZIP (Manifest V3 + workaround)
|
||||||
@@ -43,7 +68,7 @@ jobs:
|
|||||||
cp manifest.opera.json manifest.json
|
cp manifest.opera.json manifest.json
|
||||||
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip" \
|
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip" \
|
||||||
manifest.json newtab.html src/js/*.js src/js/opera/ src/css/ assets/ _locales/ \
|
manifest.json newtab.html src/js/*.js src/js/opera/ src/css/ assets/ _locales/ \
|
||||||
-x "*.git*" "dist/*" ".github/*" "manifest.chrome-backup.json" "manifest.opera.json"
|
-x "*.git*" "dist/*" ".github/*" ".gitea/*" "manifest.chrome-backup.json" "manifest.opera.json"
|
||||||
mv manifest.chrome-backup.json manifest.json
|
mv manifest.chrome-backup.json manifest.json
|
||||||
|
|
||||||
- name: Generate SHA256 checksums
|
- name: Generate SHA256 checksums
|
||||||
@@ -52,10 +77,19 @@ jobs:
|
|||||||
sha256sum *.zip > checksums-sha256.txt
|
sha256sum *.zip > checksums-sha256.txt
|
||||||
cat checksums-sha256.txt
|
cat checksums-sha256.txt
|
||||||
|
|
||||||
- name: Create GitHub Release
|
# Gitea-native Release-Action. Legt das Release an, falls der Tag noch
|
||||||
uses: softprops/action-gh-release@v2
|
# keins hat, oder aktualisiert das bestehende und haengt die Assets an.
|
||||||
|
# Der auto-injizierte GITHUB_TOKEN auf Gitea Actions hat Gitea-API-Scope
|
||||||
|
# und reicht fuer Release-Write.
|
||||||
|
- name: Attach to Gitea release
|
||||||
|
uses: https://gitea.com/actions/release-action@main
|
||||||
with:
|
with:
|
||||||
name: "Hellion NewTab ${{ steps.version.outputs.tag }}"
|
files: |-
|
||||||
|
dist/hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip
|
||||||
|
dist/hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip
|
||||||
|
dist/hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip
|
||||||
|
dist/checksums-sha256.txt
|
||||||
|
api_key: ${{ secrets.GITHUB_TOKEN }}
|
||||||
body: |
|
body: |
|
||||||
## Hellion NewTab ${{ steps.version.outputs.tag }}
|
## Hellion NewTab ${{ steps.version.outputs.tag }}
|
||||||
|
|
||||||
@@ -64,13 +98,7 @@ jobs:
|
|||||||
- **Firefox:** `hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip`
|
- **Firefox:** `hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip`
|
||||||
- **Opera / Opera GX:** `hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip`
|
- **Opera / Opera GX:** `hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip`
|
||||||
|
|
||||||
See [README](README.md) for the full installation instructions.
|
Vollstaendige Installationsanleitung siehe README.
|
||||||
|
|
||||||
### Checksums
|
### Checksums
|
||||||
See `checksums-sha256.txt` to verify file integrity.
|
`checksums-sha256.txt` zum Verifizieren der Dateiintegritaet.
|
||||||
files: |
|
|
||||||
dist/hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip
|
|
||||||
dist/hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip
|
|
||||||
dist/hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip
|
|
||||||
dist/checksums-sha256.txt
|
|
||||||
generate_release_notes: true
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Sicherheitsprüfung — läuft bei Push und PR auf main/master
|
|
||||||
name: Security Scan
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main, master]
|
|
||||||
pull_request:
|
|
||||||
branches: [main, master]
|
|
||||||
schedule:
|
|
||||||
# Wöchentlich Montag 06:00 UTC
|
|
||||||
- cron: '0 6 * * 1'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
codeql:
|
|
||||||
name: CodeQL Analysis
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v3
|
|
||||||
with:
|
|
||||||
languages: javascript
|
|
||||||
|
|
||||||
- name: Run CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v3
|
|
||||||
|
|
||||||
dependency-review:
|
|
||||||
name: Dependency Review
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
||||||
|
|
||||||
- name: Dependency Review
|
|
||||||
uses: actions/dependency-review-action@v4
|
|
||||||
@@ -6,6 +6,23 @@ All notable changes per version. Format based on [Keep a Changelog](https://keep
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2.2.0] — 2026-06-13
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **View Transitions** — Native cross-fade on theme switch and central modals (Settings, Theme-Picker, custom dialogs, bookmark import, add-board, add-bookmark, rename). Feature-detected via `document.startViewTransition`, instant swap on older browsers. Widgets, notebook sidebar and onboarding deliberately excluded.
|
||||||
|
- **`color-scheme: dark`** — Declares the dark UA scheme so native scrollbars and form controls match the dark themes.
|
||||||
|
- **Accessibility pass** — `role="dialog"` / `aria-modal` / `aria-labelledby` on Settings and Theme-Picker with new focus trap, Escape handling and focus return; `role="toolbar"` + per-button `aria-label` on the widget toolbar; keyboard-operable theme cards (`role="button"`, `tabindex`, Enter/Space); `role="switch"` + `aria-checked` on settings toggles; focusable boards and bookmarks; visible `:focus-visible` ring tinted in the theme accent. New ARIA strings run through the i18n pipeline. Verified with Lighthouse and the axe DevTools extension, not a formal WCAG 2.2 AA audit.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **`color-mix()` token refactor** — Accent-derived color tokens now computed via `color-mix()` from `var(--accent)`, classified per theme (formula vs. override). Theme-specific alpha values and real special colors stay overrides; no visible theme change. `--border-accent` `:root` drift (179,92,255 → 179,89,255) fixed at both the Nebula block and the `:root` default.
|
||||||
|
- **`@layer` cascade ordering** — CSS reorganized into six layers (base / theme / layout / components / theme-overrides / utilities) so theme component overrides win deterministically instead of relying on selector specificity.
|
||||||
|
- **`clamp()` fluid typography** — Clock, logo, board titles and main spacing scale fluidly via `clamp()`. Existing 768px / 480px breakpoints kept as a safety net.
|
||||||
|
|
||||||
|
### Accessibility
|
||||||
|
- **`prefers-reduced-motion`** — Unlayered `@media` block disables transitions and animations, including the `::view-transition-*` pseudo-elements. The 350ms widget teardown fallback timer is retained so widgets still close when `transitionend` no longer fires under reduced motion.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [2.1.0] — 2026-04-16
|
## [2.1.0] — 2026-04-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "__MSG_extName__",
|
"name": "__MSG_extName__",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"description": "__MSG_extDesc__",
|
"description": "__MSG_extDesc__",
|
||||||
"author": "Hellion Online Media - Florian Wathling",
|
"author": "Hellion Online Media - Florian Wathling",
|
||||||
"homepage_url": "https://hellion-media.de",
|
"homepage_url": "https://hellion-media.de",
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "__MSG_extName__",
|
"name": "__MSG_extName__",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"description": "__MSG_extDesc__",
|
"description": "__MSG_extDesc__",
|
||||||
"author": "Hellion Online Media - Florian Wathling",
|
"author": "Hellion Online Media - Florian Wathling",
|
||||||
"homepage_url": "https://hellion-media.de",
|
"homepage_url": "https://hellion-media.de",
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "__MSG_extName__",
|
"name": "__MSG_extName__",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"description": "__MSG_extDesc__",
|
"description": "__MSG_extDesc__",
|
||||||
"author": "Hellion Online Media - Florian Wathling",
|
"author": "Hellion Online Media - Florian Wathling",
|
||||||
"homepage_url": "https://hellion-media.de",
|
"homepage_url": "https://hellion-media.de",
|
||||||
|
|||||||
+24
-24
@@ -60,7 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- WIDGET TOOLBAR -->
|
<!-- WIDGET TOOLBAR -->
|
||||||
<div class="widget-toolbar" id="widgetToolbar">
|
<div class="widget-toolbar" id="widgetToolbar" role="toolbar" aria-orientation="vertical" data-i18n-aria-label="toolbar.label">
|
||||||
<button class="widget-toolbar-btn" data-action="new-note" data-i18n-title="toolbar.note" title="Note erstellen">
|
<button class="widget-toolbar-btn" data-action="new-note" data-i18n-title="toolbar.note" title="Note erstellen">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
@@ -103,9 +103,9 @@
|
|||||||
|
|
||||||
<!-- SETTINGS PANEL -->
|
<!-- SETTINGS PANEL -->
|
||||||
<div class="panel-overlay" id="settingsOverlay"></div>
|
<div class="panel-overlay" id="settingsOverlay"></div>
|
||||||
<aside class="settings-panel" id="settingsPanel">
|
<aside class="settings-panel" id="settingsPanel" role="dialog" aria-modal="true" aria-labelledby="settingsPanelTitle" aria-hidden="true">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<span data-i18n="settings.title">Einstellungen</span>
|
<span id="settingsPanelTitle" data-i18n="settings.title">Einstellungen</span>
|
||||||
<button class="btn-close" id="btnCloseSettings" data-i18n-title="dialog.close">✕</button>
|
<button class="btn-close" id="btnCloseSettings" data-i18n-title="dialog.close">✕</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
<span class="setting-desc" data-i18n="settings.image_ref.desc">Bilder als Referenz anzeigen (nur aktuelle Session)</span>
|
<span class="setting-desc" data-i18n="settings.image_ref.desc">Bilder als Referenz anzeigen (nur aktuelle Session)</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle">
|
<label class="toggle">
|
||||||
<input type="checkbox" id="settingImageRef">
|
<input type="checkbox" id="settingImageRef" role="switch" aria-checked="false">
|
||||||
<span class="slider"></span>
|
<span class="slider"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
<div class="about-block">
|
<div class="about-block">
|
||||||
<div class="about-logo" data-i18n="about.title">⬡ HELLION NEWTAB</div>
|
<div class="about-logo" data-i18n="about.title">⬡ HELLION NEWTAB</div>
|
||||||
<div class="about-version">Version 2.1.0 · by Hellion Online Media</div>
|
<div class="about-version">Version 2.2.0 · by Hellion Online Media</div>
|
||||||
|
|
||||||
<div class="about-links">
|
<div class="about-links">
|
||||||
<a href="https://hellion-media.de/impressum" target="_blank" class="about-link">
|
<a href="https://hellion-media.de/impressum" target="_blank" class="about-link">
|
||||||
@@ -291,63 +291,63 @@
|
|||||||
|
|
||||||
<!-- THEME PICKER MODAL -->
|
<!-- THEME PICKER MODAL -->
|
||||||
<div class="modal-overlay" id="themeOverlay">
|
<div class="modal-overlay" id="themeOverlay">
|
||||||
<div class="theme-modal" id="themeModal">
|
<div class="theme-modal" id="themeModal" role="dialog" aria-modal="true" aria-labelledby="themeModalTitle" aria-hidden="true">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span data-i18n="modal.theme_header">Darstellung</span>
|
<span id="themeModalTitle" data-i18n="modal.theme_header">Darstellung</span>
|
||||||
<button class="btn-close" id="btnCloseTheme" data-i18n-title="dialog.close">✕</button>
|
<button class="btn-close" id="btnCloseTheme" data-i18n-title="dialog.close">✕</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-grid">
|
<div class="theme-grid">
|
||||||
<div class="theme-card active" data-value="nebula">
|
<div class="theme-card active" data-value="nebula" role="button" tabindex="0" aria-pressed="true" data-i18n-aria-label="theme.card.nebula">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-nebula.webp" alt="Nebula" />
|
<img class="theme-card-img" src="assets/themes/bg-nebula.webp" alt="Nebula" />
|
||||||
<span class="theme-card-label">Nebula</span>
|
<span class="theme-card-label">Nebula</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="crescent">
|
<div class="theme-card" data-value="crescent" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.crescent">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-crescent.webp" alt="Crescent" />
|
<img class="theme-card-img" src="assets/themes/bg-crescent.webp" alt="Crescent" />
|
||||||
<span class="theme-card-label">Crescent</span>
|
<span class="theme-card-label">Crescent</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="event-horizon">
|
<div class="theme-card" data-value="event-horizon" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.event_horizon">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-event-horizon.webp" alt="Event Horizon" />
|
<img class="theme-card-img" src="assets/themes/bg-event-horizon.webp" alt="Event Horizon" />
|
||||||
<span class="theme-card-label">Event Horizon</span>
|
<span class="theme-card-label">Event Horizon</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="merchantman">
|
<div class="theme-card" data-value="merchantman" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.merchantman">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-merchantman.webp" alt="Merchantman" />
|
<img class="theme-card-img" src="assets/themes/bg-merchantman.webp" alt="Merchantman" />
|
||||||
<span class="theme-card-label">Merchantman</span>
|
<span class="theme-card-label">Merchantman</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="julia-jin">
|
<div class="theme-card" data-value="julia-jin" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.julia_jin">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-julia-jin.webp" alt="Julia & Jin" />
|
<img class="theme-card-img" src="assets/themes/bg-julia-jin.webp" alt="Julia & Jin" />
|
||||||
<span class="theme-card-label">Julia & Jin</span>
|
<span class="theme-card-label">Julia & Jin</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="sc-sunset">
|
<div class="theme-card" data-value="sc-sunset" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.sc_sunset">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-sc-sunset.webp" alt="SC Sunset" />
|
<img class="theme-card-img" src="assets/themes/bg-sc-sunset.webp" alt="SC Sunset" />
|
||||||
<span class="theme-card-label">SC Sunset</span>
|
<span class="theme-card-label">SC Sunset</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="hellion-hud">
|
<div class="theme-card" data-value="hellion-hud" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.hellion_hud">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-hellion-hud.webp" alt="Hellion HUD" />
|
<img class="theme-card-img" src="assets/themes/bg-hellion-hud.webp" alt="Hellion HUD" />
|
||||||
<span class="theme-card-label">HUD</span>
|
<span class="theme-card-label">HUD</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="hellion-energy">
|
<div class="theme-card" data-value="hellion-energy" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.hellion_energy">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-hellion-energy.webp" alt="Hellion Energy" />
|
<img class="theme-card-img" src="assets/themes/bg-hellion-energy.webp" alt="Hellion Energy" />
|
||||||
<span class="theme-card-label">Energy</span>
|
<span class="theme-card-label">Energy</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="satisfactory">
|
<div class="theme-card" data-value="satisfactory" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.satisfactory">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-satisfactory.webp" alt="Satisfactory" />
|
<img class="theme-card-img" src="assets/themes/bg-satisfactory.webp" alt="Satisfactory" />
|
||||||
<span class="theme-card-label">Satisfactory</span>
|
<span class="theme-card-label">Satisfactory</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="avorion">
|
<div class="theme-card" data-value="avorion" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.avorion">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-avorion.webp" alt="Avorion" />
|
<img class="theme-card-img" src="assets/themes/bg-avorion.webp" alt="Avorion" />
|
||||||
<span class="theme-card-label">Avorion</span>
|
<span class="theme-card-label">Avorion</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="hellion-stealth">
|
<div class="theme-card" data-value="hellion-stealth" role="button" tabindex="0" aria-pressed="false" data-i18n-aria-label="theme.card.hellion_stealth">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-scPolaris.webp" alt="Hellion Stealth" />
|
<img class="theme-card-img" src="assets/themes/bg-scPolaris.webp" alt="Hellion Stealth" />
|
||||||
<span class="theme-card-label">Stealth</span>
|
<span class="theme-card-label">Stealth</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="theme-card-check">✓</span>
|
||||||
@@ -382,42 +382,42 @@
|
|||||||
<span class="setting-label" data-i18n="settings.compact">Kompaktmodus</span>
|
<span class="setting-label" data-i18n="settings.compact">Kompaktmodus</span>
|
||||||
<span class="setting-desc" data-i18n="settings.compact.desc">Weniger Abstand für mehr Bookmarks</span>
|
<span class="setting-desc" data-i18n="settings.compact.desc">Weniger Abstand für mehr Bookmarks</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle"><input type="checkbox" id="settingCompact" /><span class="slider"></span></label>
|
<label class="toggle"><input type="checkbox" id="settingCompact" role="switch" aria-checked="false" /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<span class="setting-label" data-i18n="settings.shorten">Lange Titel kürzen</span>
|
<span class="setting-label" data-i18n="settings.shorten">Lange Titel kürzen</span>
|
||||||
<span class="setting-desc" data-i18n="settings.shorten.desc">Titel auf eine Zeile mit „…" kürzen</span>
|
<span class="setting-desc" data-i18n="settings.shorten.desc">Titel auf eine Zeile mit „…" kürzen</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle"><input type="checkbox" id="settingShorten" /><span class="slider"></span></label>
|
<label class="toggle"><input type="checkbox" id="settingShorten" role="switch" aria-checked="false" /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<span class="setting-label" data-i18n="settings.search">Suchleiste anzeigen</span>
|
<span class="setting-label" data-i18n="settings.search">Suchleiste anzeigen</span>
|
||||||
<span class="setting-desc" data-i18n="settings.search.desc">Suchleiste unter dem Header ein/aus</span>
|
<span class="setting-desc" data-i18n="settings.search.desc">Suchleiste unter dem Header ein/aus</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle"><input type="checkbox" id="settingShowSearch" checked /><span class="slider"></span></label>
|
<label class="toggle"><input type="checkbox" id="settingShowSearch" role="switch" aria-checked="true" checked /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<span class="setting-label" data-i18n="settings.newtab">Links in neuem Tab</span>
|
<span class="setting-label" data-i18n="settings.newtab">Links in neuem Tab</span>
|
||||||
<span class="setting-desc" data-i18n="settings.newtab.desc">Bookmarks in neuem Browser-Tab öffnen</span>
|
<span class="setting-desc" data-i18n="settings.newtab.desc">Bookmarks in neuem Browser-Tab öffnen</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle"><input type="checkbox" id="settingNewTab" checked /><span class="slider"></span></label>
|
<label class="toggle"><input type="checkbox" id="settingNewTab" role="switch" aria-checked="true" checked /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<span class="setting-label" data-i18n="settings.showdesc">Beschreibungen anzeigen</span>
|
<span class="setting-label" data-i18n="settings.showdesc">Beschreibungen anzeigen</span>
|
||||||
<span class="setting-desc" data-i18n="settings.showdesc.desc">Gespeicherte Beschreibung unter Bookmarks</span>
|
<span class="setting-desc" data-i18n="settings.showdesc.desc">Gespeicherte Beschreibung unter Bookmarks</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle"><input type="checkbox" id="settingShowDesc" /><span class="slider"></span></label>
|
<label class="toggle"><input type="checkbox" id="settingShowDesc" role="switch" aria-checked="false" /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<span class="setting-label" data-i18n="settings.hideextra">Bookmarks ausblenden</span>
|
<span class="setting-label" data-i18n="settings.hideextra">Bookmarks ausblenden</span>
|
||||||
<span class="setting-desc" data-i18n="settings.hideextra.desc">Überzählige Bookmarks in langen Boards verstecken</span>
|
<span class="setting-desc" data-i18n="settings.hideextra.desc">Überzählige Bookmarks in langen Boards verstecken</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle"><input type="checkbox" id="settingHideExtra" /><span class="slider"></span></label>
|
<label class="toggle"><input type="checkbox" id="settingHideExtra" role="switch" aria-checked="false" /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row" id="visibleCountRow">
|
<div class="setting-row" id="visibleCountRow">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
|
|||||||
+1
-2
@@ -44,8 +44,7 @@
|
|||||||
],
|
],
|
||||||
"vulnerabilityAlerts": {
|
"vulnerabilityAlerts": {
|
||||||
"labels": ["security", "vulnerability"],
|
"labels": ["security", "vulnerability"],
|
||||||
"schedule": ["at any time"],
|
"schedule": ["at any time"]
|
||||||
"prPriority": 10
|
|
||||||
},
|
},
|
||||||
"lockFileMaintenance": {
|
"lockFileMaintenance": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|||||||
+172
-119
@@ -1,3 +1,12 @@
|
|||||||
|
/* =============================================
|
||||||
|
CASCADE LAYERS (v2.2) — Reihenfolge = Prioritaet (spaeter gewinnt)
|
||||||
|
theme-overrides MUSS nach components stehen, sonst verlieren die
|
||||||
|
theme-spezifischen Komponenten-Regeln (Board-Blur, Cinzel-Logos,
|
||||||
|
Hover-Tints) und alle 11 Themes brechen.
|
||||||
|
prefers-reduced-motion bleibt bewusst UNGESCHICHTET (Phase 4).
|
||||||
|
============================================= */
|
||||||
|
@layer base, theme, layout, components, theme-overrides, utilities;
|
||||||
|
|
||||||
/* =============================================
|
/* =============================================
|
||||||
HELLION Dashboard — Theme System
|
HELLION Dashboard — Theme System
|
||||||
Themes: nebula | crescent | event-horizon | merchantman | julia-jin | sc-sunset | hellion-hud | hellion-energy
|
Themes: nebula | crescent | event-horizon | merchantman | julia-jin | sc-sunset | hellion-hud | hellion-energy
|
||||||
@@ -9,6 +18,7 @@
|
|||||||
- Inter: Fließtext und allgemeine Lesbarkeit
|
- Inter: Fließtext und allgemeine Lesbarkeit
|
||||||
- Cinzel: Alternative Display-Schriftart für bestimmte Themes
|
- Cinzel: Alternative Display-Schriftart für bestimmte Themes
|
||||||
============================================= */
|
============================================= */
|
||||||
|
@layer base {
|
||||||
/* Rajdhani - Lokal */
|
/* Rajdhani - Lokal */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Rajdhani';
|
font-family: 'Rajdhani';
|
||||||
@@ -46,9 +56,15 @@
|
|||||||
/* ---- BASE VARIABLES (Nebula = Default) ---- */
|
/* ---- BASE VARIABLES (Nebula = Default) ---- */
|
||||||
:root {
|
:root {
|
||||||
--accent: #b359ff;
|
--accent: #b359ff;
|
||||||
--accent-dim: rgba(179, 89, 255, 0.12);
|
/* Akzent-Töne als Formel aus --accent abgeleitet (Spec Block 1, color-mix Mittelweg).
|
||||||
--accent-glow: rgba(179, 89, 255, 0.05);
|
--*-pct ist die Pro-Theme-Alpha-Variable; Default = häufigster Wert über alle 11 Themes.
|
||||||
--border-accent: rgba(179, 92, 255, 0.25);
|
Themes mit abweichendem Alpha überschreiben nur die Prozent-Variable im theme-Layer. */
|
||||||
|
--accent-dim-pct: 12%;
|
||||||
|
--accent-glow-pct: 8%;
|
||||||
|
--border-accent-pct: 25%;
|
||||||
|
--accent-dim: color-mix(in srgb, var(--accent) var(--accent-dim-pct), transparent);
|
||||||
|
--accent-glow: color-mix(in srgb, var(--accent) var(--accent-glow-pct), transparent);
|
||||||
|
--border-accent: color-mix(in srgb, var(--accent) var(--border-accent-pct), transparent);
|
||||||
--bg-primary: #050308;
|
--bg-primary: #050308;
|
||||||
--bg-board: rgba(10, 6, 14, 0.55);
|
--bg-board: rgba(10, 6, 14, 0.55);
|
||||||
--border: rgba(255, 255, 255, 0.06);
|
--border: rgba(255, 255, 255, 0.06);
|
||||||
@@ -61,14 +77,18 @@
|
|||||||
--radius: 8px;
|
--radius: 8px;
|
||||||
--radius-sm: 5px;
|
--radius-sm: 5px;
|
||||||
--board-width: 240px;
|
--board-width: 240px;
|
||||||
--spacing: 10px;
|
--spacing: clamp(0.5rem, 0.4583rem + 0.14vw, 0.625rem);
|
||||||
--spacing-compact: 5px;
|
--spacing-compact: 5px;
|
||||||
--overlay-bg: radial-gradient(circle at center, rgba(10,6,14,0.3) 0%, rgba(5,3,8,0.85) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(10,6,14,0.3) 0%, rgba(5,3,8,0.85) 100%);
|
||||||
--header-bg: rgba(10,6,14,0.90);
|
--header-bg: rgba(10,6,14,0.90);
|
||||||
--board-hover-border: rgba(179,89,255,0.18);
|
--board-hover-border-pct: 22%;
|
||||||
|
--logo-shadow-pct: 45%;
|
||||||
|
--toggle-on-bg-pct: 20%;
|
||||||
|
--board-hover-border: color-mix(in srgb, var(--accent) var(--board-hover-border-pct), transparent);
|
||||||
--toggle-on-bg: rgba(214,92,255,0.22);
|
--toggle-on-bg: rgba(214,92,255,0.22);
|
||||||
--logo-shadow: rgba(179,89,255,0.35);
|
--logo-shadow: color-mix(in srgb, var(--accent) var(--logo-shadow-pct), transparent);
|
||||||
--bg-solid-fallback: #0a060e;
|
--bg-solid-fallback: #0a060e;
|
||||||
|
color-scheme: dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fallback fuer Browser die backdrop-filter blockieren (z.B. Brave Shields) */
|
/* Fallback fuer Browser die backdrop-filter blockieren (z.B. Brave Shields) */
|
||||||
@@ -82,15 +102,17 @@
|
|||||||
background-color: var(--bg-solid-fallback, var(--bg-primary));
|
background-color: var(--bg-solid-fallback, var(--bg-primary));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer theme {
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: NEBULA (magenta / cosmic nebula)
|
THEME: NEBULA (magenta / cosmic nebula)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="nebula"] {
|
[data-theme="nebula"] {
|
||||||
--accent: #b359ff;
|
--accent: #b359ff;
|
||||||
--accent-dim: rgba(179, 89, 255, 0.12);
|
--accent-glow-pct: 5%;
|
||||||
--accent-glow: rgba(179, 89, 255, 0.05);
|
--board-hover-border-pct: 18%;
|
||||||
--border-accent: rgba(179, 92, 255, 0.25);
|
--logo-shadow-pct: 35%;
|
||||||
--bg-primary: #050308;
|
--bg-primary: #050308;
|
||||||
--bg-board: rgba(10, 6, 14, 0.55);
|
--bg-board: rgba(10, 6, 14, 0.55);
|
||||||
--border: rgba(255, 255, 255, 0.055);
|
--border: rgba(255, 255, 255, 0.055);
|
||||||
@@ -101,22 +123,18 @@
|
|||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: radial-gradient(circle at center, rgba(10,6,14,0.3) 0%, rgba(5,3,8,0.85) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(10,6,14,0.3) 0%, rgba(5,3,8,0.85) 100%);
|
||||||
--header-bg: rgba(10,6,14,0.92);
|
--header-bg: rgba(10,6,14,0.92);
|
||||||
--board-hover-border: rgba(179,89,255,0.18);
|
|
||||||
--toggle-on-bg: rgba(214,92,255,0.22);
|
--toggle-on-bg: rgba(214,92,255,0.22);
|
||||||
--logo-shadow: rgba(179,89,255,0.35);
|
|
||||||
--bg-solid-fallback: #0a060e;
|
--bg-solid-fallback: #0a060e;
|
||||||
}
|
}
|
||||||
[data-theme="nebula"] .board { border-color: rgba(214,92,255,0.10); }
|
|
||||||
[data-theme="nebula"] .bm-item:hover { background: rgba(214,92,255,0.05); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: CRESCENT (gold / minimalist night)
|
THEME: CRESCENT (gold / minimalist night)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="crescent"] {
|
[data-theme="crescent"] {
|
||||||
--accent: #d4bd8a;
|
--accent: #d4bd8a;
|
||||||
--accent-dim: rgba(212, 189, 138, 0.12);
|
--accent-glow-pct: 5%;
|
||||||
--accent-glow: rgba(212, 189, 138, 0.05);
|
--board-hover-border-pct: 20%;
|
||||||
--border-accent: rgba(212, 189, 138, 0.25);
|
--logo-shadow-pct: 40%;
|
||||||
--bg-primary: #06080f;
|
--bg-primary: #06080f;
|
||||||
--bg-board: rgba(8, 12, 22, 0.45);
|
--bg-board: rgba(8, 12, 22, 0.45);
|
||||||
--border: rgba(212, 189, 138, 0.10);
|
--border: rgba(212, 189, 138, 0.10);
|
||||||
@@ -127,27 +145,17 @@
|
|||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: radial-gradient(circle at center, rgba(6,8,15,0.2) 0%, rgba(6,8,15,0.9) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(6,8,15,0.2) 0%, rgba(6,8,15,0.9) 100%);
|
||||||
--header-bg: rgba(6,8,15,0.95);
|
--header-bg: rgba(6,8,15,0.95);
|
||||||
--board-hover-border: rgba(212, 189, 138, 0.20);
|
|
||||||
--toggle-on-bg: rgba(200,168,74,0.22);
|
--toggle-on-bg: rgba(200,168,74,0.22);
|
||||||
--logo-shadow: rgba(212, 189, 138, 0.40);
|
|
||||||
--bg-solid-fallback: #080c16;
|
--bg-solid-fallback: #080c16;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="crescent"] .logo { font-family: 'Cinzel', serif; letter-spacing: 4px; }
|
|
||||||
[data-theme="crescent"] .clock { font-family: 'Cinzel', serif; }
|
|
||||||
[data-theme="crescent"] .board-title { letter-spacing: 2px; }
|
|
||||||
[data-theme="crescent"] .bm-item:hover { background: rgba(200,168,74,0.05); }
|
|
||||||
[data-theme="crescent"] .board { border-color: rgba(200,168,74,0.10); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: EVENT HORIZON (Cosmic Purple / deep space)
|
THEME: EVENT HORIZON (Cosmic Purple / deep space)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="event-horizon"] {
|
[data-theme="event-horizon"] {
|
||||||
--accent: #9d5cff;
|
--accent: #9d5cff;
|
||||||
--accent-dim: rgba(157, 92, 255, 0.12);
|
--border-accent-pct: 28%;
|
||||||
--accent-glow: rgba(157, 92, 255, 0.08);
|
|
||||||
--border-accent: rgba(157, 92, 255, 0.28);
|
|
||||||
--bg-primary: #040308;
|
--bg-primary: #040308;
|
||||||
--bg-board: rgba(8, 5, 15, 0.45);
|
--bg-board: rgba(8, 5, 15, 0.45);
|
||||||
--border: rgba(157, 92, 255, 0.12);
|
--border: rgba(157, 92, 255, 0.12);
|
||||||
@@ -158,23 +166,17 @@
|
|||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: radial-gradient(circle at center, rgba(4,3,8,0.2) 0%, rgba(4,3,8,0.95) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(4,3,8,0.2) 0%, rgba(4,3,8,0.95) 100%);
|
||||||
--header-bg: rgba(4,3,8,0.96);
|
--header-bg: rgba(4,3,8,0.96);
|
||||||
--board-hover-border: rgba(157, 92, 255, 0.22);
|
|
||||||
--toggle-on-bg: rgba(224,128,48,0.22);
|
--toggle-on-bg: rgba(224,128,48,0.22);
|
||||||
--logo-shadow: rgba(157, 92, 255, 0.45);
|
|
||||||
--bg-solid-fallback: #08050f;
|
--bg-solid-fallback: #08050f;
|
||||||
}
|
}
|
||||||
[data-theme="event-horizon"] .board { border-color: rgba(157, 92, 255, 0.15); }
|
|
||||||
[data-theme="event-horizon"] .bm-item:hover { background: rgba(157, 92, 255, 0.08); }
|
|
||||||
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: MERCHANTMAN (Emerald / industrial sci-fi)
|
THEME: MERCHANTMAN (Emerald / industrial sci-fi)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="merchantman"] {
|
[data-theme="merchantman"] {
|
||||||
--accent: #2eb8b8;
|
--accent: #2eb8b8;
|
||||||
--accent-dim: rgba(46, 184, 184, 0.12);
|
--accent-glow-pct: 6%;
|
||||||
--accent-glow: rgba(46, 184, 184, 0.06);
|
--board-hover-border-pct: 20%;
|
||||||
--border-accent: rgba(46, 184, 184, 0.25);
|
|
||||||
--bg-primary: #040808;
|
--bg-primary: #040808;
|
||||||
--bg-board: rgba(6, 10, 10, 0.58);
|
--bg-board: rgba(6, 10, 10, 0.58);
|
||||||
--border: rgba(46, 184, 184, 0.10);
|
--border: rgba(46, 184, 184, 0.10);
|
||||||
@@ -185,22 +187,17 @@
|
|||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: radial-gradient(circle at 30% 40%, rgba(4,8,8,0.2) 0%, rgba(4,8,8,0.92) 100%);
|
--overlay-bg: radial-gradient(circle at 30% 40%, rgba(4,8,8,0.2) 0%, rgba(4,8,8,0.92) 100%);
|
||||||
--header-bg: rgba(4,8,8,0.95);
|
--header-bg: rgba(4,8,8,0.95);
|
||||||
--board-hover-border: rgba(46, 184, 184, 0.20);
|
|
||||||
--toggle-on-bg: rgba(78,207,207,0.22);
|
--toggle-on-bg: rgba(78,207,207,0.22);
|
||||||
--logo-shadow: rgba(46, 184, 184, 0.45);
|
|
||||||
--bg-solid-fallback: #060a0a;
|
--bg-solid-fallback: #060a0a;
|
||||||
}
|
}
|
||||||
[data-theme="merchantman"] .board { border-color: rgba(46, 184, 184, 0.12); }
|
|
||||||
[data-theme="merchantman"] .bm-item:hover { background: rgba(46, 184, 184, 0.06); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: JULIA & JIN (Aetherial Night / FFXIV)
|
THEME: JULIA & JIN (Aetherial Night / FFXIV)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="julia-jin"] {
|
[data-theme="julia-jin"] {
|
||||||
--accent: #7db3ff;
|
--accent: #7db3ff;
|
||||||
--accent-dim: rgba(125, 179, 255, 0.12);
|
--border-accent-pct: 30%;
|
||||||
--accent-glow: rgba(125, 179, 255, 0.08);
|
--logo-shadow-pct: 50%;
|
||||||
--border-accent: rgba(125, 179, 255, 0.30);
|
|
||||||
--bg-primary: #03050a;
|
--bg-primary: #03050a;
|
||||||
--bg-board: rgba(7, 10, 20, 0.60);
|
--bg-board: rgba(7, 10, 20, 0.60);
|
||||||
--border: rgba(125, 179, 255, 0.12);
|
--border: rgba(125, 179, 255, 0.12);
|
||||||
@@ -211,25 +208,16 @@
|
|||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(180deg, rgba(3,5,10,0.85) 0%, rgba(3,5,10,0.25) 50%, rgba(3,5,10,0.92) 100%);
|
--overlay-bg: linear-gradient(180deg, rgba(3,5,10,0.85) 0%, rgba(3,5,10,0.25) 50%, rgba(3,5,10,0.92) 100%);
|
||||||
--header-bg: rgba(3,5,10,0.94);
|
--header-bg: rgba(3,5,10,0.94);
|
||||||
--board-hover-border: rgba(125, 179, 255, 0.22);
|
|
||||||
--toggle-on-bg: rgba(91,159,255,0.22);
|
--toggle-on-bg: rgba(91,159,255,0.22);
|
||||||
--logo-shadow: rgba(125, 179, 255, 0.50);
|
|
||||||
--bg-solid-fallback: #070a14;
|
--bg-solid-fallback: #070a14;
|
||||||
}
|
}
|
||||||
[data-theme="julia-jin"] .logo { font-family: 'Cinzel', serif; letter-spacing: 5px; text-transform: uppercase; }
|
|
||||||
[data-theme="julia-jin"] .clock { font-family: 'Cinzel', serif; font-weight: 600; }
|
|
||||||
[data-theme="julia-jin"] .board-title { letter-spacing: 2px; font-weight: 500; }
|
|
||||||
[data-theme="julia-jin"] .board { border-color: rgba(125, 179, 255, 0.15); backdrop-filter: blur(4px); }
|
|
||||||
[data-theme="julia-jin"] .bm-item:hover { background: rgba(125, 179, 255, 0.08); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: SC Sunset - Horizon Glow
|
THEME: SC Sunset - Horizon Glow
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="sc-sunset"] {
|
[data-theme="sc-sunset"] {
|
||||||
--accent: #ff8c3d;
|
--accent: #ff8c3d;
|
||||||
--accent-dim: rgba(255, 140, 61, 0.12);
|
--border-accent-pct: 28%;
|
||||||
--accent-glow: rgba(255, 140, 61, 0.08);
|
|
||||||
--border-accent: rgba(255, 140, 61, 0.28);
|
|
||||||
--bg-primary: #080503;
|
--bg-primary: #080503;
|
||||||
--bg-board: rgba(15, 10, 8, 0.55);
|
--bg-board: rgba(15, 10, 8, 0.55);
|
||||||
--border: rgba(255, 140, 61, 0.10);
|
--border: rgba(255, 140, 61, 0.10);
|
||||||
@@ -240,25 +228,19 @@
|
|||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: radial-gradient(circle at 50% 60%, rgba(8,5,3,0.1) 0%, rgba(8,5,3,0.92) 100%);
|
--overlay-bg: radial-gradient(circle at 50% 60%, rgba(8,5,3,0.1) 0%, rgba(8,5,3,0.92) 100%);
|
||||||
--header-bg: rgba(8,5,3,0.94);
|
--header-bg: rgba(8,5,3,0.94);
|
||||||
--board-hover-border: rgba(255, 140, 61, 0.22);
|
|
||||||
--toggle-on-bg: rgba(240,124,48,0.22);
|
--toggle-on-bg: rgba(240,124,48,0.22);
|
||||||
--logo-shadow: rgba(255, 140, 61, 0.45);
|
|
||||||
--bg-solid-fallback: #0f0a08;
|
--bg-solid-fallback: #0f0a08;
|
||||||
}
|
}
|
||||||
[data-theme="sc-sunset"] .board {
|
|
||||||
border-color: rgba(255, 140, 61, 0.15);
|
|
||||||
backdrop-filter: blur(6px);}
|
|
||||||
[data-theme="sc-sunset"] .bm-item:hover {
|
|
||||||
background: rgba(255, 140, 61, 0.08); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: HELLION HUD (circuit board / red+green)
|
THEME: HELLION HUD (circuit board / red+green)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="hellion-hud"] {
|
[data-theme="hellion-hud"] {
|
||||||
--accent: #32ff6a;
|
--accent: #32ff6a;
|
||||||
--accent-dim: rgba(50, 255, 106, 0.10);
|
--accent-dim-pct: 10%;
|
||||||
--accent-glow: rgba(50, 255, 106, 0.05);
|
--accent-glow-pct: 5%;
|
||||||
--border-accent: rgba(50, 255, 106, 0.25);
|
--board-hover-border-pct: 20%;
|
||||||
|
--logo-shadow-pct: 40%;
|
||||||
--bg-primary: #030503;
|
--bg-primary: #030503;
|
||||||
--bg-board: rgba(5, 8, 5, 0.65);
|
--bg-board: rgba(5, 8, 5, 0.65);
|
||||||
--border: rgba(50, 255, 106, 0.12);
|
--border: rgba(50, 255, 106, 0.12);
|
||||||
@@ -269,31 +251,19 @@
|
|||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: radial-gradient(circle at center, rgba(3,5,3,0.15) 0%, rgba(3,5,3,0.95) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(3,5,3,0.15) 0%, rgba(3,5,3,0.95) 100%);
|
||||||
--header-bg: rgba(3,5,3,0.96);
|
--header-bg: rgba(3,5,3,0.96);
|
||||||
--board-hover-border: rgba(50, 255, 106, 0.20);
|
|
||||||
--toggle-on-bg: rgba(34,204,68,0.20);
|
--toggle-on-bg: rgba(34,204,68,0.20);
|
||||||
--logo-shadow: rgba(50, 255, 106, 0.40);
|
|
||||||
--bg-solid-fallback: #050805;
|
--bg-solid-fallback: #050805;
|
||||||
--danger: #ff4d4d;
|
--danger: #ff4d4d;
|
||||||
}
|
}
|
||||||
[data-theme="hellion-hud"] .board {
|
|
||||||
border-color: rgba(50, 255, 106, 0.15);
|
|
||||||
backdrop-filter: blur(8px) brightness(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="hellion-hud"] .clock {
|
|
||||||
font-family: 'Rajdhani', sans-serif;
|
|
||||||
font-weight: 700;
|
|
||||||
text-shadow: 0 0 10px var(--accent-glow);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: HELLION ENERGY (matrix / tactical green)
|
THEME: HELLION ENERGY (matrix / tactical green)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="hellion-energy"] {
|
[data-theme="hellion-energy"] {
|
||||||
--accent: #1eff8e;
|
--accent: #1eff8e;
|
||||||
--accent-dim: rgba(30, 255, 142, 0.12);
|
--border-accent-pct: 30%;
|
||||||
--accent-glow: rgba(30, 255, 142, 0.08);
|
--board-hover-border-pct: 25%;
|
||||||
--border-accent: rgba(30, 255, 142, 0.30);
|
--logo-shadow-pct: 60%;
|
||||||
--bg-primary: #020503;
|
--bg-primary: #020503;
|
||||||
--bg-board: rgba(4, 7, 5, 0.60);
|
--bg-board: rgba(4, 7, 5, 0.60);
|
||||||
--border: rgba(30, 255, 142, 0.12);
|
--border: rgba(30, 255, 142, 0.12);
|
||||||
@@ -304,29 +274,18 @@
|
|||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: radial-gradient(circle at center, rgba(2,5,3,0.1) 0%, rgba(2,5,3,0.95) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(2,5,3,0.1) 0%, rgba(2,5,3,0.95) 100%);
|
||||||
--header-bg: rgba(2,5,3,0.96);
|
--header-bg: rgba(2,5,3,0.96);
|
||||||
--board-hover-border: rgba(30, 255, 142, 0.25);
|
|
||||||
--toggle-on-bg: rgba(0,232,122,0.18);
|
--toggle-on-bg: rgba(0,232,122,0.18);
|
||||||
--logo-shadow: rgba(30, 255, 142, 0.60);
|
|
||||||
--bg-solid-fallback: #040705;
|
--bg-solid-fallback: #040705;
|
||||||
}
|
}
|
||||||
[data-theme="hellion-energy"] .board {
|
|
||||||
border-color: rgba(30, 255, 142, 0.15);
|
|
||||||
backdrop-filter: blur(10px) saturate(1.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="hellion-energy"] .bm-item:hover {
|
|
||||||
background: rgba(30, 255, 142, 0.10);
|
|
||||||
box-shadow: inset 0 0 10px rgba(30, 255, 142, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: SATISFACTORY (Industrial Desert)
|
THEME: SATISFACTORY (Industrial Desert)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="satisfactory"] {
|
[data-theme="satisfactory"] {
|
||||||
--accent: #00b4d8;
|
--accent: #00b4d8;
|
||||||
--accent-dim: rgba(0, 180, 216, 0.12);
|
--border-accent-pct: 35%;
|
||||||
--accent-glow: rgba(0, 180, 216, 0.08);
|
--board-hover-border-pct: 25%;
|
||||||
--border-accent: rgba(0, 180, 216, 0.35);
|
--logo-shadow-pct: 40%;
|
||||||
--bg-primary: #1a0f08;
|
--bg-primary: #1a0f08;
|
||||||
--bg-board: rgba(26, 15, 8, 0.65);
|
--bg-board: rgba(26, 15, 8, 0.65);
|
||||||
--border: rgba(0, 180, 216, 0.15);
|
--border: rgba(0, 180, 216, 0.15);
|
||||||
@@ -340,25 +299,17 @@
|
|||||||
rgba(26,15,8,0.15) 50%,
|
rgba(26,15,8,0.15) 50%,
|
||||||
rgba(26,15,8,0.90) 100%);
|
rgba(26,15,8,0.90) 100%);
|
||||||
--header-bg: rgba(26,15,8,0.95);
|
--header-bg: rgba(26,15,8,0.95);
|
||||||
--board-hover-border: rgba(0, 180, 216, 0.25);
|
--toggle-on-bg: color-mix(in srgb, var(--accent) var(--toggle-on-bg-pct), transparent);
|
||||||
--toggle-on-bg: rgba(0, 180, 216, 0.20);
|
|
||||||
--logo-shadow: rgba(0, 180, 216, 0.40);
|
|
||||||
--bg-solid-fallback: #1a0f08;
|
--bg-solid-fallback: #1a0f08;
|
||||||
}
|
}
|
||||||
[data-theme="satisfactory"] .logo { font-family: 'Rajdhani', sans-serif; font-weight: 700; letter-spacing: 3px; text-transform: uppercase; }
|
|
||||||
[data-theme="satisfactory"] .clock { font-family: 'Rajdhani', sans-serif; font-weight: 600; color: var(--accent); }
|
|
||||||
[data-theme="satisfactory"] .board-title { font-family: 'Rajdhani', sans-serif; letter-spacing: 1.5px; text-transform: uppercase; }
|
|
||||||
[data-theme="satisfactory"] .board { border-color: rgba(0, 180, 216, 0.20); backdrop-filter: blur(12px); }
|
|
||||||
[data-theme="satisfactory"] .bm-item:hover { background: rgba(0, 180, 216, 0.10); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: AVORION (Deep Void)
|
THEME: AVORION (Deep Void)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="avorion"] {
|
[data-theme="avorion"] {
|
||||||
--accent: #2ec4a0;
|
--accent: #2ec4a0;
|
||||||
--accent-dim: rgba(46, 196, 160, 0.12);
|
--border-accent-pct: 30%;
|
||||||
--accent-glow: rgba(46, 196, 160, 0.08);
|
--logo-shadow-pct: 50%;
|
||||||
--border-accent: rgba(46, 196, 160, 0.30);
|
|
||||||
--bg-primary: #020d0c;
|
--bg-primary: #020d0c;
|
||||||
--bg-board: rgba(2, 13, 12, 0.60);
|
--bg-board: rgba(2, 13, 12, 0.60);
|
||||||
--border: rgba(46, 196, 160, 0.12);
|
--border: rgba(46, 196, 160, 0.12);
|
||||||
@@ -371,25 +322,18 @@
|
|||||||
transparent 0%,
|
transparent 0%,
|
||||||
rgba(2, 13, 12, 0.95) 100%);
|
rgba(2, 13, 12, 0.95) 100%);
|
||||||
--header-bg: rgba(2, 13, 12, 0.94);
|
--header-bg: rgba(2, 13, 12, 0.94);
|
||||||
--board-hover-border: rgba(46, 196, 160, 0.22);
|
--toggle-on-bg-pct: 18%;
|
||||||
--toggle-on-bg: rgba(46, 196, 160, 0.18);
|
--toggle-on-bg: color-mix(in srgb, var(--accent) var(--toggle-on-bg-pct), transparent);
|
||||||
--logo-shadow: rgba(46, 196, 160, 0.50);
|
|
||||||
--bg-solid-fallback: #020d0c;
|
--bg-solid-fallback: #020d0c;
|
||||||
}
|
}
|
||||||
[data-theme="avorion"] .logo { font-family: 'Rajdhani', sans-serif; font-weight: 500; letter-spacing: 6px; text-transform: uppercase; }
|
|
||||||
[data-theme="avorion"] .clock { font-family: 'Rajdhani', sans-serif; font-weight: 400; color: var(--accent); }
|
|
||||||
[data-theme="avorion"] .board-title { font-family: 'Rajdhani', sans-serif; font-weight: 500; text-transform: uppercase; }
|
|
||||||
[data-theme="avorion"] .board { border-color: rgba(46, 196, 160, 0.15); backdrop-filter: blur(8px); }
|
|
||||||
[data-theme="avorion"] .bm-item:hover { background: rgba(46, 196, 160, 0.08); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: HELLION STEALTH (Tactical Recon)
|
THEME: HELLION STEALTH (Tactical Recon)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="hellion-stealth"] {
|
[data-theme="hellion-stealth"] {
|
||||||
--accent: #5ec2ff;
|
--accent: #5ec2ff;
|
||||||
--accent-dim: rgba(94, 194, 255, 0.12);
|
--border-accent-pct: 35%;
|
||||||
--accent-glow: rgba(94, 194, 255, 0.08);
|
--board-hover-border-pct: 25%;
|
||||||
--border-accent: rgba(94, 194, 255, 0.35);
|
|
||||||
--bg-primary: #0d0f12;
|
--bg-primary: #0d0f12;
|
||||||
--bg-board: rgba(13, 15, 18, 0.70);
|
--bg-board: rgba(13, 15, 18, 0.70);
|
||||||
--border: rgba(94, 194, 255, 0.15);
|
--border: rgba(94, 194, 255, 0.15);
|
||||||
@@ -402,21 +346,80 @@
|
|||||||
transparent 0%,
|
transparent 0%,
|
||||||
rgba(13, 15, 18, 0.90) 100%);
|
rgba(13, 15, 18, 0.90) 100%);
|
||||||
--header-bg: rgba(13, 15, 18, 0.96);
|
--header-bg: rgba(13, 15, 18, 0.96);
|
||||||
--board-hover-border: rgba(94, 194, 255, 0.25);
|
--toggle-on-bg: color-mix(in srgb, var(--accent) var(--toggle-on-bg-pct), transparent);
|
||||||
--toggle-on-bg: rgba(94, 194, 255, 0.20);
|
|
||||||
--logo-shadow: rgba(94, 194, 255, 0.45);
|
|
||||||
--bg-solid-fallback: #0d0f12;
|
--bg-solid-fallback: #0d0f12;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
THEME-SCOPED KOMPONENTEN-REGELN (Sammelblock fuer @layer theme-overrides)
|
||||||
|
Aus den 11 [data-theme]-Bloecken extrahiert. Muss spaeter als components
|
||||||
|
greifen, sonst verlieren Board-Blur, Cinzel-Logos und Hover-Tints.
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
@layer theme-overrides {
|
||||||
|
[data-theme="nebula"] .board { border-color: rgba(214,92,255,0.10); }
|
||||||
|
[data-theme="nebula"] .bm-item:hover { background: rgba(214,92,255,0.05); }
|
||||||
|
[data-theme="crescent"] .logo { font-family: 'Cinzel', serif; letter-spacing: 4px; }
|
||||||
|
[data-theme="crescent"] .clock { font-family: 'Cinzel', serif; }
|
||||||
|
[data-theme="crescent"] .board-title { letter-spacing: 2px; }
|
||||||
|
[data-theme="crescent"] .bm-item:hover { background: rgba(200,168,74,0.05); }
|
||||||
|
[data-theme="crescent"] .board { border-color: rgba(200,168,74,0.10); }
|
||||||
|
[data-theme="event-horizon"] .board { border-color: rgba(157, 92, 255, 0.15); }
|
||||||
|
[data-theme="event-horizon"] .bm-item:hover { background: rgba(157, 92, 255, 0.08); }
|
||||||
|
[data-theme="merchantman"] .board { border-color: rgba(46, 184, 184, 0.12); }
|
||||||
|
[data-theme="merchantman"] .bm-item:hover { background: rgba(46, 184, 184, 0.06); }
|
||||||
|
[data-theme="julia-jin"] .logo { font-family: 'Cinzel', serif; letter-spacing: 5px; text-transform: uppercase; }
|
||||||
|
[data-theme="julia-jin"] .clock { font-family: 'Cinzel', serif; font-weight: 600; }
|
||||||
|
[data-theme="julia-jin"] .board-title { letter-spacing: 2px; font-weight: 500; }
|
||||||
|
[data-theme="julia-jin"] .board { border-color: rgba(125, 179, 255, 0.15); backdrop-filter: blur(4px); }
|
||||||
|
[data-theme="julia-jin"] .bm-item:hover { background: rgba(125, 179, 255, 0.08); }
|
||||||
|
[data-theme="sc-sunset"] .board {
|
||||||
|
border-color: rgba(255, 140, 61, 0.15);
|
||||||
|
backdrop-filter: blur(6px);}
|
||||||
|
[data-theme="sc-sunset"] .bm-item:hover {
|
||||||
|
background: rgba(255, 140, 61, 0.08); }
|
||||||
|
[data-theme="hellion-hud"] .board {
|
||||||
|
border-color: rgba(50, 255, 106, 0.15);
|
||||||
|
backdrop-filter: blur(8px) brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="hellion-hud"] .clock {
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
text-shadow: 0 0 10px var(--accent-glow);
|
||||||
|
}
|
||||||
|
[data-theme="hellion-energy"] .board {
|
||||||
|
border-color: rgba(30, 255, 142, 0.15);
|
||||||
|
backdrop-filter: blur(10px) saturate(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="hellion-energy"] .bm-item:hover {
|
||||||
|
background: rgba(30, 255, 142, 0.10);
|
||||||
|
box-shadow: inset 0 0 10px rgba(30, 255, 142, 0.05);
|
||||||
|
}
|
||||||
|
[data-theme="satisfactory"] .logo { font-family: 'Rajdhani', sans-serif; font-weight: 700; letter-spacing: 3px; text-transform: uppercase; }
|
||||||
|
[data-theme="satisfactory"] .clock { font-family: 'Rajdhani', sans-serif; font-weight: 600; color: var(--accent); }
|
||||||
|
[data-theme="satisfactory"] .board-title { font-family: 'Rajdhani', sans-serif; letter-spacing: 1.5px; text-transform: uppercase; }
|
||||||
|
[data-theme="satisfactory"] .board { border-color: rgba(0, 180, 216, 0.20); backdrop-filter: blur(12px); }
|
||||||
|
[data-theme="satisfactory"] .bm-item:hover { background: rgba(0, 180, 216, 0.10); }
|
||||||
|
[data-theme="avorion"] .logo { font-family: 'Rajdhani', sans-serif; font-weight: 500; letter-spacing: 6px; text-transform: uppercase; }
|
||||||
|
[data-theme="avorion"] .clock { font-family: 'Rajdhani', sans-serif; font-weight: 400; color: var(--accent); }
|
||||||
|
[data-theme="avorion"] .board-title { font-family: 'Rajdhani', sans-serif; font-weight: 500; text-transform: uppercase; }
|
||||||
|
[data-theme="avorion"] .board { border-color: rgba(46, 196, 160, 0.15); backdrop-filter: blur(8px); }
|
||||||
|
[data-theme="avorion"] .bm-item:hover { background: rgba(46, 196, 160, 0.08); }
|
||||||
[data-theme="hellion-stealth"] .logo { font-family: 'Rajdhani', sans-serif; font-weight: 700; letter-spacing: 4px; }
|
[data-theme="hellion-stealth"] .logo { font-family: 'Rajdhani', sans-serif; font-weight: 700; letter-spacing: 4px; }
|
||||||
[data-theme="hellion-stealth"] .clock { font-family: 'Rajdhani', sans-serif; font-weight: 600; color: var(--accent); }
|
[data-theme="hellion-stealth"] .clock { font-family: 'Rajdhani', sans-serif; font-weight: 600; color: var(--accent); }
|
||||||
[data-theme="hellion-stealth"] .board-title { text-transform: uppercase; font-size: 0.85rem; letter-spacing: 2px; }
|
[data-theme="hellion-stealth"] .board-title { text-transform: uppercase; font-size: 0.85rem; letter-spacing: 2px; }
|
||||||
[data-theme="hellion-stealth"] .board { border-color: rgba(94, 194, 255, 0.15); backdrop-filter: blur(10px); }
|
[data-theme="hellion-stealth"] .board { border-color: rgba(94, 194, 255, 0.15); backdrop-filter: blur(10px); }
|
||||||
[data-theme="hellion-stealth"] .bm-item:hover { background: rgba(94, 194, 255, 0.10); border-left: 2px solid var(--accent); }
|
[data-theme="hellion-stealth"] .bm-item:hover { background: rgba(94, 194, 255, 0.10); border-left: 2px solid var(--accent); }
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
BASE STYLES
|
BASE STYLES
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|
||||||
|
@layer base {
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
@@ -428,7 +431,9 @@ html, body {
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
transition: background 0.5s;
|
transition: background 0.5s;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer layout {
|
||||||
.bg-layer {
|
.bg-layer {
|
||||||
position: fixed; inset: 0; z-index: 0;
|
position: fixed; inset: 0; z-index: 0;
|
||||||
background-size: cover; background-position: center;
|
background-size: cover; background-position: center;
|
||||||
@@ -462,7 +467,7 @@ html, body {
|
|||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 17px; font-weight: 700; letter-spacing: 3px;
|
font-size: clamp(0.875rem, 0.8125rem + 0.21vw, 1.0625rem); font-weight: 700; letter-spacing: 3px;
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
text-shadow: 0 0 24px var(--logo-shadow);
|
text-shadow: 0 0 24px var(--logo-shadow);
|
||||||
transition: color 0.5s, text-shadow 0.5s, font-family 0.1s;
|
transition: color 0.5s, text-shadow 0.5s, font-family 0.1s;
|
||||||
@@ -475,7 +480,7 @@ html, body {
|
|||||||
|
|
||||||
.clock {
|
.clock {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 20px; font-weight: 500; letter-spacing: 2px;
|
font-size: clamp(1rem, 0.9167rem + 0.28vw, 1.25rem); font-weight: 500; letter-spacing: 2px;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
transition: color 0.5s, font-family 0.1s;
|
transition: color 0.5s, font-family 0.1s;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
@@ -515,7 +520,9 @@ html, body {
|
|||||||
padding: 110px 40px 40px;
|
padding: 110px 40px 40px;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
.board {
|
.board {
|
||||||
width: var(--board-width);
|
width: var(--board-width);
|
||||||
background: var(--bg-board);
|
background: var(--bg-board);
|
||||||
@@ -555,7 +562,7 @@ html, body {
|
|||||||
|
|
||||||
.board-title {
|
.board-title {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 13px; font-weight: 600; letter-spacing: 1.5px;
|
font-size: clamp(0.6875rem, 0.6458rem + 0.14vw, 0.8125rem); font-weight: 600; letter-spacing: 1.5px;
|
||||||
color: var(--accent); text-transform: uppercase;
|
color: var(--accent); text-transform: uppercase;
|
||||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||||
max-width: 160px;
|
max-width: 160px;
|
||||||
@@ -845,9 +852,11 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
background: var(--accent-dim);
|
background: var(--accent-dim);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ---- BOARD BLUR (Private Mode) ---- */
|
/* ---- BOARD BLUR (Private Mode) ---- */
|
||||||
|
@layer utilities {
|
||||||
.board.blurred .board-list,
|
.board.blurred .board-list,
|
||||||
.board.blurred .show-more-btn,
|
.board.blurred .show-more-btn,
|
||||||
.board.blurred .add-bm-btn {
|
.board.blurred .add-bm-btn {
|
||||||
@@ -890,8 +899,10 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
}
|
}
|
||||||
.btn-blur-board:hover { background: var(--accent-dim); color: var(--accent); }
|
.btn-blur-board:hover { background: var(--accent-dim); color: var(--accent); }
|
||||||
.board.blurred .btn-blur-board { color: var(--accent); opacity: 0.7; }
|
.board.blurred .btn-blur-board { color: var(--accent); opacity: 0.7; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@layer components {
|
||||||
/* ---- ABOUT BLOCK ---- */
|
/* ---- ABOUT BLOCK ---- */
|
||||||
.about-block {
|
.about-block {
|
||||||
padding: 4px 18px 14px;
|
padding: 4px 18px 14px;
|
||||||
@@ -2343,10 +2354,12 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
.settings-section.open .section-content {
|
.settings-section.open .section-content {
|
||||||
max-height: 800px;
|
max-height: 800px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
UTILITY CLASSES
|
UTILITY CLASSES
|
||||||
============================================ */
|
============================================ */
|
||||||
|
@layer utilities {
|
||||||
.hidden { display: none; }
|
.hidden { display: none; }
|
||||||
.accent-text { color: var(--accent); }
|
.accent-text { color: var(--accent); }
|
||||||
.dim { opacity: 0.4; }
|
.dim { opacity: 0.4; }
|
||||||
@@ -2360,6 +2373,27 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
.about-info-label-block { display: block; margin-bottom: 6px; }
|
.about-info-label-block { display: block; margin-bottom: 6px; }
|
||||||
.about-link-subtle { color: var(--text-secondary); text-decoration: none; }
|
.about-link-subtle { color: var(--text-secondary); text-decoration: none; }
|
||||||
.modal-input-spaced { margin-top: 8px; }
|
.modal-input-spaced { margin-top: 8px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
A11Y FOCUS RING (v2.2)
|
||||||
|
============================================ */
|
||||||
|
@layer utilities {
|
||||||
|
/* A11y: sichtbarer Fokus-Ring, getoent im Theme-Akzent.
|
||||||
|
Liegt bewusst im spaeten utilities-Layer, damit er die 9 outline:none-Regeln
|
||||||
|
aus dem components-Layer ueber die Kaskaden-Ordnung schlaegt (Spezifitaet
|
||||||
|
zwischen Layern irrelevant). Nur :focus-visible, damit Maus-Klicks keinen Ring zeigen. */
|
||||||
|
:focus-visible {
|
||||||
|
outline: 2px solid color-mix(in srgb, var(--accent) 70%, white 30%);
|
||||||
|
outline-offset: 2px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
/* Bookmarks/Cards: Ring leicht enger, da sie eigene Radien haben */
|
||||||
|
.bm-item:focus-visible,
|
||||||
|
.theme-card:focus-visible {
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
RESPONSIVE — Mobile & Tablet
|
RESPONSIVE — Mobile & Tablet
|
||||||
@@ -2430,3 +2464,22 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
|
|
||||||
.modal { width: calc(100vw - 32px); }
|
.modal { width: calc(100vw - 32px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =============================================
|
||||||
|
prefers-reduced-motion — UNGESCHICHTET (kein @layer).
|
||||||
|
Ungeschichtete Regeln gewinnen ueber alle Layer.
|
||||||
|
Kappt alle Transitions/Animationen inkl. der
|
||||||
|
View-Transition-Pseudo-Elemente (der *-Selektor
|
||||||
|
trifft sie nicht zuverlaessig, daher explizit).
|
||||||
|
============================================= */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*, *::before, *::after {
|
||||||
|
transition: none !important;
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
::view-transition-group(*),
|
||||||
|
::view-transition-old(*),
|
||||||
|
::view-transition-new(*) {
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -105,7 +105,7 @@ async function checkBackupReminder() {
|
|||||||
const notesData = (widgetData && Array.isArray(widgetData.notes)) ? widgetData.notes : [];
|
const notesData = (widgetData && Array.isArray(widgetData.notes)) ? widgetData.notes : [];
|
||||||
const calcHistory = (widgetData && widgetData.calculator) ? widgetData.calculator.history || [] : [];
|
const calcHistory = (widgetData && widgetData.calculator) ? widgetData.calculator.history || [] : [];
|
||||||
const timerPresets = (widgetData && widgetData.timer) ? widgetData.timer.presets || [] : [];
|
const timerPresets = (widgetData && widgetData.timer) ? widgetData.timer.presets || [] : [];
|
||||||
const data = { version: '2.1.0', exported: new Date().toISOString(), boards, settings, notes: notesData, calculator: calcHistory, timerPresets };
|
const data = { version: '2.2.0', exported: new Date().toISOString(), boards, settings, notes: notesData, calculator: calcHistory, timerPresets };
|
||||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
|
|||||||
+25
-2
@@ -214,6 +214,9 @@ function createBmEl(bm) {
|
|||||||
li.dataset.bmId = bm.id;
|
li.dataset.bmId = bm.id;
|
||||||
li.dataset.bmUrl = bm.url;
|
li.dataset.bmUrl = bm.url;
|
||||||
li.draggable = true;
|
li.draggable = true;
|
||||||
|
li.setAttribute('role', 'link');
|
||||||
|
li.setAttribute('tabindex', '0');
|
||||||
|
li.setAttribute('aria-label', bm.title);
|
||||||
|
|
||||||
const favicon = document.createElement('div');
|
const favicon = document.createElement('div');
|
||||||
favicon.className = 'bm-favicon-local';
|
favicon.className = 'bm-favicon-local';
|
||||||
@@ -267,11 +270,31 @@ function bindBoardListEvents(list, board) {
|
|||||||
window.open(url, settings.newTab ? '_blank' : '_self', 'noopener,noreferrer');
|
window.open(url, settings.newTab ? '_blank' : '_self', 'noopener,noreferrer');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Tastatur: Enter oeffnet den Bookmark wie ein Klick. role="link" erwartet
|
||||||
|
// nur Enter (Space ist Button-Semantik). Der Delete-Button bleibt ein echter
|
||||||
|
// <button> und feuert seinen eigenen Klick ueber Space/Enter selbst.
|
||||||
|
list.addEventListener('keydown', e => {
|
||||||
|
if (e.key !== 'Enter') return;
|
||||||
|
const bmItem = e.target.closest('.bm-item');
|
||||||
|
if (!bmItem || e.target !== bmItem) return; // nur wenn der li selbst fokussiert ist
|
||||||
|
e.preventDefault();
|
||||||
|
const url = bmItem.dataset.bmUrl;
|
||||||
|
if (url) {
|
||||||
|
window.open(url, settings.newTab ? '_blank' : '_self', 'noopener,noreferrer');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- MODALS ----
|
// ---- MODALS ----
|
||||||
function openModal(id) { document.getElementById(id).classList.add('active'); }
|
// reduced-motion kappt das Fade ueber den ungeschichteten @media-Block.
|
||||||
function closeModal(id) { document.getElementById(id).classList.remove('active'); }
|
// Feature-Detection-Fallback (Firefox < 144): instant.
|
||||||
|
function openModal(id) {
|
||||||
|
withViewTransition(() => document.getElementById(id).classList.add('active'));
|
||||||
|
}
|
||||||
|
function closeModal(id) {
|
||||||
|
withViewTransition(() => document.getElementById(id).classList.remove('active'));
|
||||||
|
}
|
||||||
|
|
||||||
function openAddBoardModal() {
|
function openAddBoardModal() {
|
||||||
document.getElementById('newBoardName').value = '';
|
document.getElementById('newBoardName').value = '';
|
||||||
|
|||||||
@@ -196,16 +196,18 @@ const BrowserBookmarkImport = {
|
|||||||
overlay.appendChild(modal);
|
overlay.appendChild(modal);
|
||||||
document.body.appendChild(overlay);
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
// Animation
|
// View-Transition-Fade
|
||||||
requestAnimationFrame(() => overlay.classList.add('active'));
|
withViewTransition(() => overlay.classList.add('active'));
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Schliesst das Modal */
|
/** Schliesst das Modal */
|
||||||
_closeModal() {
|
_closeModal() {
|
||||||
const overlay = document.getElementById('bmImportOverlay');
|
const overlay = document.getElementById('bmImportOverlay');
|
||||||
if (!overlay) return;
|
if (!overlay) return;
|
||||||
overlay.classList.remove('active');
|
withViewTransition(() => {
|
||||||
setTimeout(() => overlay.remove(), 250);
|
overlay.classList.remove('active');
|
||||||
|
overlay.remove();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ function initDataButtons() {
|
|||||||
btnExport.addEventListener('click', async () => {
|
btnExport.addEventListener('click', async () => {
|
||||||
const widgetData = await Store.get('widgetStates');
|
const widgetData = await Store.get('widgetStates');
|
||||||
const data = {
|
const data = {
|
||||||
version: '2.1.0',
|
version: '2.2.0',
|
||||||
exported: new Date().toISOString(),
|
exported: new Date().toISOString(),
|
||||||
boards,
|
boards,
|
||||||
settings,
|
settings,
|
||||||
|
|||||||
+30
-4
@@ -40,23 +40,34 @@ const HellionDialog = {
|
|||||||
*/
|
*/
|
||||||
_show(config) {
|
_show(config) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
|
const prevFocus = document.activeElement;
|
||||||
const overlay = document.createElement('div');
|
const overlay = document.createElement('div');
|
||||||
overlay.className = 'dialog-overlay';
|
overlay.className = 'dialog-overlay';
|
||||||
|
|
||||||
const box = document.createElement('div');
|
const box = document.createElement('div');
|
||||||
box.className = 'dialog-box';
|
box.className = 'dialog-box';
|
||||||
|
box.setAttribute('role', config.isConfirm ? 'alertdialog' : 'dialog');
|
||||||
|
box.setAttribute('aria-modal', 'true');
|
||||||
|
// Eindeutige IDs pro Dialog-Instanz: kurz gestapelte Dialoge (timer.js/
|
||||||
|
// image-ref.js feuern teils ohne await) duerfen sich keine festen IDs
|
||||||
|
// teilen, sonst liest der Screenreader ueber aria-* den falschen Titel.
|
||||||
|
const uid = 'dlg-' + Date.now().toString(36) + '-' + (HellionDialog._seq = (HellionDialog._seq || 0) + 1);
|
||||||
|
box.setAttribute('aria-labelledby', uid + '-title');
|
||||||
|
box.setAttribute('aria-describedby', uid + '-body');
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
const header = document.createElement('div');
|
const header = document.createElement('div');
|
||||||
header.className = 'dialog-header';
|
header.className = 'dialog-header';
|
||||||
header.appendChild(this._createIcon(config.type));
|
header.appendChild(this._createIcon(config.type));
|
||||||
const titleSpan = document.createElement('span');
|
const titleSpan = document.createElement('span');
|
||||||
|
titleSpan.id = uid + '-title';
|
||||||
titleSpan.textContent = config.title;
|
titleSpan.textContent = config.title;
|
||||||
header.appendChild(titleSpan);
|
header.appendChild(titleSpan);
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
const body = document.createElement('div');
|
const body = document.createElement('div');
|
||||||
body.className = 'dialog-body';
|
body.className = 'dialog-body';
|
||||||
|
body.id = uid + '-body';
|
||||||
body.textContent = config.message;
|
body.textContent = config.message;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
@@ -64,9 +75,12 @@ const HellionDialog = {
|
|||||||
actions.className = 'dialog-actions';
|
actions.className = 'dialog-actions';
|
||||||
|
|
||||||
function cleanup(result) {
|
function cleanup(result) {
|
||||||
overlay.classList.remove('active');
|
|
||||||
document.removeEventListener('keydown', keyHandler);
|
document.removeEventListener('keydown', keyHandler);
|
||||||
setTimeout(() => overlay.remove(), 200);
|
withViewTransition(() => {
|
||||||
|
overlay.classList.remove('active');
|
||||||
|
overlay.remove();
|
||||||
|
});
|
||||||
|
if (prevFocus && typeof prevFocus.focus === 'function') prevFocus.focus();
|
||||||
resolve(result);
|
resolve(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,12 +118,24 @@ const HellionDialog = {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
cleanup(config.isConfirm ? false : undefined);
|
cleanup(config.isConfirm ? false : undefined);
|
||||||
}
|
}
|
||||||
|
if (e.key === 'Tab') {
|
||||||
|
// Fokus-Falle: nur die Buttons im actions-Container sind fokussierbar
|
||||||
|
const items = Array.from(actions.querySelectorAll('button'));
|
||||||
|
if (items.length === 0) return;
|
||||||
|
const first = items[0];
|
||||||
|
const last = items[items.length - 1];
|
||||||
|
if (e.shiftKey && document.activeElement === first) {
|
||||||
|
e.preventDefault(); last.focus();
|
||||||
|
} else if (!e.shiftKey && document.activeElement === last) {
|
||||||
|
e.preventDefault(); first.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
document.addEventListener('keydown', keyHandler);
|
document.addEventListener('keydown', keyHandler);
|
||||||
|
|
||||||
document.body.appendChild(overlay);
|
document.body.appendChild(overlay);
|
||||||
// Nächster Frame für CSS-Transition
|
// View-Transition uebernimmt das Fade; Fokus bleibt erhalten
|
||||||
requestAnimationFrame(() => {
|
withViewTransition(() => {
|
||||||
overlay.classList.add('active');
|
overlay.classList.add('active');
|
||||||
confirmBtn.focus();
|
confirmBtn.focus();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -387,6 +387,19 @@ const STRINGS = {
|
|||||||
'modal.rename_placeholder': 'Neuer Name...',
|
'modal.rename_placeholder': 'Neuer Name...',
|
||||||
'modal.rename_confirm': 'Umbenennen',
|
'modal.rename_confirm': 'Umbenennen',
|
||||||
'modal.theme_header': 'Darstellung',
|
'modal.theme_header': 'Darstellung',
|
||||||
|
// Theme-Picker-Cards (ARIA)
|
||||||
|
'theme.card.nebula': 'Theme Nebula wählen',
|
||||||
|
'theme.card.crescent': 'Theme Crescent wählen',
|
||||||
|
'theme.card.event_horizon': 'Theme Event Horizon wählen',
|
||||||
|
'theme.card.merchantman': 'Theme Merchantman wählen',
|
||||||
|
'theme.card.julia_jin': 'Theme Julia & Jin wählen',
|
||||||
|
'theme.card.sc_sunset': 'Theme SC Sunset wählen',
|
||||||
|
'theme.card.hellion_hud': 'Theme Hellion HUD wählen',
|
||||||
|
'theme.card.hellion_energy': 'Theme Hellion Energy wählen',
|
||||||
|
'theme.card.satisfactory': 'Theme Satisfactory wählen',
|
||||||
|
'theme.card.avorion': 'Theme Avorion wählen',
|
||||||
|
'theme.card.hellion_stealth': 'Theme Hellion Stealth wählen',
|
||||||
|
'toolbar.label': 'Widget-Werkzeugleiste',
|
||||||
|
|
||||||
// About
|
// About
|
||||||
'about.title': '⬡ HELLION NEWTAB',
|
'about.title': '⬡ HELLION NEWTAB',
|
||||||
@@ -799,6 +812,19 @@ const STRINGS = {
|
|||||||
'modal.rename_placeholder': 'New name...',
|
'modal.rename_placeholder': 'New name...',
|
||||||
'modal.rename_confirm': 'Rename',
|
'modal.rename_confirm': 'Rename',
|
||||||
'modal.theme_header': 'Theme',
|
'modal.theme_header': 'Theme',
|
||||||
|
// Theme picker cards (ARIA)
|
||||||
|
'theme.card.nebula': 'Select Nebula theme',
|
||||||
|
'theme.card.crescent': 'Select Crescent theme',
|
||||||
|
'theme.card.event_horizon': 'Select Event Horizon theme',
|
||||||
|
'theme.card.merchantman': 'Select Merchantman theme',
|
||||||
|
'theme.card.julia_jin': 'Select Julia & Jin theme',
|
||||||
|
'theme.card.sc_sunset': 'Select SC Sunset theme',
|
||||||
|
'theme.card.hellion_hud': 'Select Hellion HUD theme',
|
||||||
|
'theme.card.hellion_energy': 'Select Hellion Energy theme',
|
||||||
|
'theme.card.satisfactory': 'Select Satisfactory theme',
|
||||||
|
'theme.card.avorion': 'Select Avorion theme',
|
||||||
|
'theme.card.hellion_stealth': 'Select Hellion Stealth theme',
|
||||||
|
'toolbar.label': 'Widget toolbar',
|
||||||
|
|
||||||
// About
|
// About
|
||||||
'about.title': '⬡ HELLION NEWTAB',
|
'about.title': '⬡ HELLION NEWTAB',
|
||||||
@@ -865,6 +891,10 @@ function applyLanguage() {
|
|||||||
el.title = text;
|
el.title = text;
|
||||||
el.setAttribute('aria-label', text);
|
el.setAttribute('aria-label', text);
|
||||||
});
|
});
|
||||||
|
// aria-label ohne sichtbaren title (z.B. Theme-Cards)
|
||||||
|
document.querySelectorAll('[data-i18n-aria-label]').forEach(el => {
|
||||||
|
el.setAttribute('aria-label', t(el.dataset.i18nAriaLabel));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+112
-15
@@ -3,24 +3,105 @@
|
|||||||
Settings Panel, Theme-Modal, Accordion, Toggles
|
Settings Panel, Theme-Modal, Accordion, Toggles
|
||||||
============================================= */
|
============================================= */
|
||||||
|
|
||||||
|
// ---- A11Y: Fokus-Management fuer Modals ----
|
||||||
|
// Merkt sich das vor dem Oeffnen fokussierte Element, damit wir es beim
|
||||||
|
// Schliessen restaurieren koennen. Pro offenem Modal eine Closure-Variable.
|
||||||
|
const _focusReturn = { settings: null, theme: null };
|
||||||
|
|
||||||
|
/** Liefert die fokussierbaren Elemente innerhalb eines Containers. */
|
||||||
|
function _focusable(container) {
|
||||||
|
return Array.from(container.querySelectorAll(
|
||||||
|
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
||||||
|
)).filter(el => el.offsetParent !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tab/Shift+Tab im Container einfangen + Escape schliesst. */
|
||||||
|
function _makeTrap(container, closeFn) {
|
||||||
|
return function trap(e) {
|
||||||
|
// Ein offener HellionDialog (z.B. Reset-All-Confirm oder BG-URL-Alert aus
|
||||||
|
// dem Panel) hat Vorrang: sein eigener keydown-Handler uebernimmt Escape/Tab.
|
||||||
|
// Sonst schloessen beide Listener gleichzeitig und die Dialog-Fokusfalle wird loechrig.
|
||||||
|
if (document.querySelector('.dialog-overlay')) return;
|
||||||
|
if (e.key === 'Escape') { e.preventDefault(); closeFn(); return; }
|
||||||
|
if (e.key !== 'Tab') return;
|
||||||
|
const items = _focusable(container);
|
||||||
|
if (items.length === 0) return;
|
||||||
|
const first = items[0];
|
||||||
|
const last = items[items.length - 1];
|
||||||
|
if (e.shiftKey && document.activeElement === first) {
|
||||||
|
e.preventDefault(); last.focus();
|
||||||
|
} else if (!e.shiftKey && document.activeElement === last) {
|
||||||
|
e.preventDefault(); first.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ---- SETTINGS PANEL ----
|
// ---- SETTINGS PANEL ----
|
||||||
|
// Hinweis: withViewTransition (Phase 4) bleibt fuer das Fade erhalten; das
|
||||||
|
// Fokus-Management (merken, Falle, Rueckgabe) liegt bewusst ausserhalb des
|
||||||
|
// Transition-Callbacks. activeElement wird vor der Mutation gelesen.
|
||||||
|
let _settingsTrap = null;
|
||||||
function openSettings() {
|
function openSettings() {
|
||||||
document.getElementById('settingsPanel').classList.add('open');
|
const panel = document.getElementById('settingsPanel');
|
||||||
document.getElementById('settingsOverlay').classList.add('active');
|
_focusReturn.settings = document.activeElement;
|
||||||
|
withViewTransition(() => {
|
||||||
|
panel.classList.add('open');
|
||||||
|
document.getElementById('settingsOverlay').classList.add('active');
|
||||||
|
});
|
||||||
|
panel.setAttribute('aria-hidden', 'false');
|
||||||
|
_settingsTrap = _makeTrap(panel, closeSettings);
|
||||||
|
document.addEventListener('keydown', _settingsTrap);
|
||||||
|
const first = _focusable(panel)[0];
|
||||||
|
if (first) first.focus();
|
||||||
}
|
}
|
||||||
function closeSettings() {
|
function closeSettings() {
|
||||||
document.getElementById('settingsPanel').classList.remove('open');
|
const panel = document.getElementById('settingsPanel');
|
||||||
document.getElementById('settingsOverlay').classList.remove('active');
|
withViewTransition(() => {
|
||||||
|
panel.classList.remove('open');
|
||||||
|
document.getElementById('settingsOverlay').classList.remove('active');
|
||||||
|
});
|
||||||
|
panel.setAttribute('aria-hidden', 'true');
|
||||||
|
if (_settingsTrap) { document.removeEventListener('keydown', _settingsTrap); _settingsTrap = null; }
|
||||||
|
if (_focusReturn.settings) { _focusReturn.settings.focus(); _focusReturn.settings = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- THEME MODAL ----
|
// ---- THEME MODAL ----
|
||||||
|
let _themeTrap = null;
|
||||||
function openThemeModal() {
|
function openThemeModal() {
|
||||||
const overlay = document.getElementById('themeOverlay');
|
const overlay = document.getElementById('themeOverlay');
|
||||||
overlay.classList.add('active');
|
const modal = document.getElementById('themeModal');
|
||||||
|
_focusReturn.theme = document.activeElement;
|
||||||
|
withViewTransition(() => {
|
||||||
|
overlay.classList.add('active');
|
||||||
|
});
|
||||||
|
modal.setAttribute('aria-hidden', 'false');
|
||||||
|
_themeTrap = _makeTrap(modal, closeThemeModal);
|
||||||
|
document.addEventListener('keydown', _themeTrap);
|
||||||
|
const first = _focusable(modal)[0];
|
||||||
|
if (first) first.focus();
|
||||||
}
|
}
|
||||||
function closeThemeModal() {
|
function closeThemeModal() {
|
||||||
const overlay = document.getElementById('themeOverlay');
|
const overlay = document.getElementById('themeOverlay');
|
||||||
overlay.classList.remove('active');
|
const modal = document.getElementById('themeModal');
|
||||||
|
withViewTransition(() => {
|
||||||
|
overlay.classList.remove('active');
|
||||||
|
});
|
||||||
|
modal.setAttribute('aria-hidden', 'true');
|
||||||
|
if (_themeTrap) { document.removeEventListener('keydown', _themeTrap); _themeTrap = null; }
|
||||||
|
if (_focusReturn.theme) { _focusReturn.theme.focus(); _focusReturn.theme = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wechselt das Theme mit nativem Cross-Fade (View Transitions API).
|
||||||
|
* Wrap sitzt bewusst hier am User-Ausloeser, NICHT in applyTheme(),
|
||||||
|
* sonst fadet jeder neue Tab beim Initial-Load (settings.js:101).
|
||||||
|
* Feature-Detection-Fallback: aeltere Browser (z.B. Firefox < 144)
|
||||||
|
* schalten instant um, ohne Bruch.
|
||||||
|
* @param {string} name - Theme-Name
|
||||||
|
*/
|
||||||
|
function switchTheme(name) {
|
||||||
|
const swap = () => applyTheme(name, false); // false: Theme-BG anwenden (kein User-bgUrl-Schutz hier noetig, bgUrl wurde geleert)
|
||||||
|
withViewTransition(swap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,6 +170,11 @@ function applySettings() {
|
|||||||
const imgRefBtn = document.querySelector('[data-action="image-ref"]');
|
const imgRefBtn = document.querySelector('[data-action="image-ref"]');
|
||||||
if (imgRefBtn) imgRefBtn.classList.toggle('hidden', !settings.imageRefEnabled);
|
if (imgRefBtn) imgRefBtn.classList.toggle('hidden', !settings.imageRefEnabled);
|
||||||
|
|
||||||
|
// A11y: aria-checked aller role=switch-Toggles an den realen checked-State angleichen
|
||||||
|
document.querySelectorAll('.toggle input[role="switch"]').forEach(cb => {
|
||||||
|
cb.setAttribute('aria-checked', cb.checked ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
|
||||||
// Toolbar-Position
|
// Toolbar-Position
|
||||||
document.body.classList.toggle('toolbar-left', settings.toolbarPos === 'left');
|
document.body.classList.toggle('toolbar-left', settings.toolbarPos === 'left');
|
||||||
const toolbarPosEl = document.getElementById('settingToolbarPos');
|
const toolbarPosEl = document.getElementById('settingToolbarPos');
|
||||||
@@ -122,15 +208,25 @@ function bindSettingsEvents() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Theme-Picker (Cards im Theme-Modal)
|
// Theme-Picker (Cards im Theme-Modal)
|
||||||
document.querySelectorAll('.theme-card').forEach(card => {
|
const themeCards = document.querySelectorAll('.theme-card');
|
||||||
card.addEventListener('click', async () => {
|
function selectThemeCard(card) {
|
||||||
const name = card.dataset.value;
|
const name = card.dataset.value;
|
||||||
if (!name || name === settings.theme) return;
|
if (!name || name === settings.theme) return Promise.resolve();
|
||||||
settings.theme = name;
|
settings.theme = name;
|
||||||
settings.bgUrl = '';
|
settings.bgUrl = '';
|
||||||
document.getElementById('bgUrlInput').value = '';
|
document.getElementById('bgUrlInput').value = '';
|
||||||
applyTheme(name, false);
|
// aria-pressed synchron halten — applyTheme/switchTheme pflegt nur die .active-Klasse, nicht ARIA
|
||||||
await saveSettings();
|
themeCards.forEach(c => c.setAttribute('aria-pressed', c === card ? 'true' : 'false'));
|
||||||
|
switchTheme(name); // WICHTIG: switchTheme aus Phase 4 (View-Transition-Wrapper), NICHT applyTheme direkt — sonst geht der Theme-Fade verloren
|
||||||
|
return saveSettings();
|
||||||
|
}
|
||||||
|
themeCards.forEach(card => {
|
||||||
|
card.addEventListener('click', () => selectThemeCard(card));
|
||||||
|
card.addEventListener('keydown', e => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
selectThemeCard(card);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -163,6 +259,7 @@ function bindSettingsEvents() {
|
|||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (el) {
|
if (el) {
|
||||||
el.addEventListener('change', async e => {
|
el.addEventListener('change', async e => {
|
||||||
|
e.target.setAttribute('aria-checked', e.target.checked ? 'true' : 'false');
|
||||||
fn(e.target.checked);
|
fn(e.target.checked);
|
||||||
await saveSettings();
|
await saveSettings();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -55,3 +55,14 @@ async function saveBoards() {
|
|||||||
async function saveSettings() {
|
async function saveSettings() {
|
||||||
await Store.set('settings', settings);
|
await Store.set('settings', settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- VIEW TRANSITIONS ----
|
||||||
|
// Fuehrt eine synchrone DOM-Mutation mit nativem View-Transition-Fade aus.
|
||||||
|
// Feature-Detection-Fallback (Firefox < 144): instant. reduced-motion kappt das Fade ueber den ungeschichteten @media-Block.
|
||||||
|
function withViewTransition(mutate) {
|
||||||
|
if (document.startViewTransition) {
|
||||||
|
document.startViewTransition(mutate);
|
||||||
|
} else {
|
||||||
|
mutate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user