Makes the navbar and layout clicks
This commit is contained in:
137
src/app.scss
137
src/app.scss
@@ -12,21 +12,21 @@ body {
|
|||||||
background-position: center;
|
background-position: center;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
transition: background-color cubic-bezier(0.165, 0.84, 0.44, 1) 300ms;
|
transition: background-color cubic-bezier(0.165, 0.84, 0.44, 1) 300ms;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--link);
|
color: var(--link);
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--link-hover);
|
color: var(--link-hover);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: var(--text-title);
|
color: var(--text-title);
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
color: var(--text-subtitle);
|
color: var(--text-subtitle);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
@@ -37,69 +37,50 @@ h2 {
|
|||||||
font-size: 1.7rem;
|
font-size: 1.7rem;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
.fas,
|
|
||||||
.fab,
|
|
||||||
.far {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--highlight-secondary);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
color: var(--text-header);
|
color: var(--text-header);
|
||||||
|
|
||||||
.dashboard-title {
|
|
||||||
padding-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first-line {
|
.first-line {
|
||||||
|
background-color: var(--highlight-primary);
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
padding-top: 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
min-height: 100px;
|
min-height: 104px;
|
||||||
background-color: var(--highlight-primary);
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-top: -12px;
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
.headline {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
height: min-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
min-height: 80px;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
.logo {
|
.logo {
|
||||||
i {
|
width: 48px;
|
||||||
vertical-align: top;
|
height: 48px;
|
||||||
padding: 8px 15px;
|
}
|
||||||
font-size: 48px;
|
|
||||||
|
.dashboard-title {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 48px;
|
||||||
|
h2 {
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
h1 {
|
||||||
max-height: 70px;
|
font-size: 2em;
|
||||||
max-width: 70px;
|
line-height: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
nav.navbar {
|
||||||
background-color: var(--highlight-secondary);
|
background-color: var(--highlight-secondary);
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--text-header);
|
color: var(--text-header);
|
||||||
padding: 8px 12px;
|
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
@@ -108,12 +89,62 @@ header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-item {
|
||||||
|
.link-label {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-item:first-child {
|
||||||
|
margin-left: -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-item input {
|
||||||
|
width: 160px;
|
||||||
|
transition: width cubic-bezier(0.165, 0.84, 0.44, 1) 300ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-item input:focus {
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-menu {
|
.navbar-menu {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-end {
|
main {
|
||||||
text-align: right;
|
.layout-column {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--layout-template);
|
||||||
|
gap: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-row {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
gap: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-group {
|
||||||
|
background: lime;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-group.layout-column {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-group.layout-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--layout-template);
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
background: orange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ logo: 'icon-any.svg'
|
|||||||
icon: 'fa fa-cloud'
|
icon: 'fa fa-cloud'
|
||||||
asmask: true
|
asmask: true
|
||||||
|
|
||||||
colors:
|
links:
|
||||||
dark:
|
- title: 'Cloud'
|
||||||
test: red
|
icon: 'fa fa-cloud'
|
||||||
|
url: 'https://example.com'
|
||||||
|
- title: 'Hard drive'
|
||||||
|
icon: 'fa fa-hard-drive'
|
||||||
|
url: 'https://example.com'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- title: '/Cloud'
|
- title: '/Cloud'
|
||||||
@@ -29,3 +33,24 @@ services:
|
|||||||
url: '/pihole'
|
url: '/pihole'
|
||||||
type: 'pihole'
|
type: 'pihole'
|
||||||
keywords: 'cloud storage files'
|
keywords: 'cloud storage files'
|
||||||
|
|
||||||
|
- title: '/Cloud'
|
||||||
|
subtitle: 'Private Cloud Utilities'
|
||||||
|
icon: 'fas fa-cloud'
|
||||||
|
items:
|
||||||
|
- title: 'NAS'
|
||||||
|
subtitle: 'Network Attached Storage'
|
||||||
|
icon: 'fas fa-hard-drive'
|
||||||
|
target: '_blank'
|
||||||
|
url: '/NAS'
|
||||||
|
type: prowlarr
|
||||||
|
|
||||||
|
keywords: 'cloud storage files'
|
||||||
|
|
||||||
|
- title: 'PiHole'
|
||||||
|
subtitle: 'A DNS Hole'
|
||||||
|
icon: 'fas fa-hard-drive'
|
||||||
|
target: '_blank'
|
||||||
|
url: '/pihole'
|
||||||
|
type: 'pihole'
|
||||||
|
keywords: 'cloud storage files'
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import type { BrandConfig } from '$lib/config';
|
import type { BrandConfig } from '$lib/config';
|
||||||
|
|
||||||
export let brand: BrandConfig = {};
|
export let brand: BrandConfig = {};
|
||||||
export let size = 48;
|
|
||||||
export let color = 'var(--text)';
|
export let color = 'var(--text)';
|
||||||
console.log(brand);
|
console.log(brand);
|
||||||
|
|
||||||
@@ -11,11 +10,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div style="--mask: url({brand.logo || ''}); --mask-color: {color}" class:masked={masked()}>
|
||||||
class="container"
|
|
||||||
style="--size: {size}px; --mask: url({brand.logo || ''}); --mask-color: {color}"
|
|
||||||
class:masked={masked()}
|
|
||||||
>
|
|
||||||
{#if brand.logo}
|
{#if brand.logo}
|
||||||
{#if brand.asmask != true}
|
{#if brand.asmask != true}
|
||||||
<img src={brand.logo} alt="logo" />
|
<img src={brand.logo} alt="logo" />
|
||||||
@@ -26,25 +21,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.container {
|
div {
|
||||||
width: var(--size);
|
width: 100%;
|
||||||
min-width: min-content;
|
height: 100%;
|
||||||
height: var(--size);
|
|
||||||
color: var(--mask-color);
|
color: var(--mask-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.masked {
|
.masked {
|
||||||
background: var(--mask-color);
|
background: var(--mask-color);
|
||||||
mask: var(--mask) center no-repeat;
|
mask: var(--mask) center no-repeat;
|
||||||
mask-size: var(--size);
|
mask-size: 100%;
|
||||||
-webkit-mask: var(--mask) center no-repeat;
|
-webkit-mask: var(--mask) center no-repeat;
|
||||||
-webkit-mask-size: var(--size);
|
-webkit-mask-size: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
54
src/lib/components/Header.svelte
Normal file
54
src/lib/components/Header.svelte
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { LinkConfig, SectionConfig } from '$lib/config';
|
||||||
|
import Brand from './Brand.svelte';
|
||||||
|
import LayoutButton from './LayoutButton.svelte';
|
||||||
|
import ThemeVariantButton from './ThemeVariantButton.svelte';
|
||||||
|
|
||||||
|
export let section: SectionConfig;
|
||||||
|
export let links: Array<LinkConfig>;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<section class="first-line">
|
||||||
|
<div class="container">
|
||||||
|
<div class="logo">
|
||||||
|
<a href="/">
|
||||||
|
<Brand brand={section} color="var(--text-header)" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-title">
|
||||||
|
{#if section.subtitle}
|
||||||
|
<h2>{section.subtitle}</h2>
|
||||||
|
{/if}
|
||||||
|
<h1>{section.title}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<nav class="navbar" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-start">
|
||||||
|
{#each links as link}
|
||||||
|
<a class="navbar-item" href={link.url}>
|
||||||
|
{#if link.icon}
|
||||||
|
<i class={link.icon} />
|
||||||
|
{/if}
|
||||||
|
<span class="link-label">{link.title}</span>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<ThemeVariantButton class="navbar-item" />
|
||||||
|
|
||||||
|
<LayoutButton class="navbar-item" />
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="control has-icons-left">
|
||||||
|
<input class="input" type="text" placeholder="Filter" />
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fa fa-search" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
19
src/lib/components/LayoutButton.svelte
Normal file
19
src/lib/components/LayoutButton.svelte
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { layoutDirection } from '$lib/stores/layout';
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
if ($layoutDirection == 'column') {
|
||||||
|
layoutDirection.set('row');
|
||||||
|
} else {
|
||||||
|
layoutDirection.set('column');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a on:click={toggle} class={$$restProps.class || ''}>
|
||||||
|
{#if $layoutDirection == 'column'}
|
||||||
|
<i class="fa fa-table-columns" />
|
||||||
|
{:else}
|
||||||
|
<i class="fa fa-list-ul" />
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
@@ -8,7 +8,9 @@
|
|||||||
const component = getServiceComponent(service.type || '');
|
const component = getServiceComponent(service.type || '');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<p>{service.title}</p>
|
<div class="service-card">
|
||||||
{#if component != undefined}
|
<p>{service.title}</p>
|
||||||
<svelte:component this={component} {data} />
|
{#if component != undefined}
|
||||||
{/if}
|
<svelte:component this={component} {data} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ServiceGroupConfig } from '$lib/config';
|
import type { ServiceGroupConfig } from '$lib/config';
|
||||||
|
import { layoutDirection } from '$lib/stores/layout';
|
||||||
import ServiceCard from './ServiceCard.svelte';
|
import ServiceCard from './ServiceCard.svelte';
|
||||||
|
|
||||||
export let group: ServiceGroupConfig;
|
export let group: ServiceGroupConfig;
|
||||||
export let groupData: Array<unknown>;
|
export let groupData: Array<unknown>;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each group.items as service, i}
|
<div class="service-group layout-{$layoutDirection}">
|
||||||
<ServiceCard {service} data={groupData[i]} />
|
{#each group.items as service, i}
|
||||||
{/each}
|
<ServiceCard {service} data={groupData[i]} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { themeVariant, type ThemeVariant } from '$lib/themeVariant';
|
import { themeVariant, type ThemeVariant } from '$lib/stores/themeVariant';
|
||||||
|
|
||||||
const variants: Array<ThemeVariant> = ['default', 'dark', 'light'];
|
const variants: Array<ThemeVariant> = ['default', 'dark', 'light'];
|
||||||
|
|
||||||
@@ -9,14 +9,12 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a on:click={next}>
|
<a on:click={next} class={$$restProps.class || ''}>
|
||||||
<span class="icon">
|
{#if $themeVariant == 'default'}
|
||||||
{#if $themeVariant == 'default'}
|
<i class="fa fa-circle-half-stroke" />
|
||||||
<i class="fas fa-circle-half-stroke" />
|
{:else if $themeVariant == 'light'}
|
||||||
{:else if $themeVariant == 'light'}
|
<i class="fa-regular fa-circle" />
|
||||||
<i class="fa-regular fa-circle" />
|
{:else if $themeVariant == 'dark'}
|
||||||
{:else if $themeVariant == 'dark'}
|
<i class="fa-solid fa-circle" />
|
||||||
<i class="fa-solid fa-circle" />
|
{/if}
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ export interface SectionConfig extends BrandConfig {
|
|||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LinkConfig extends SectionConfig {
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ServiceConfig extends SectionConfig {
|
export interface ServiceConfig extends SectionConfig {
|
||||||
url: string;
|
url: string;
|
||||||
target?: string;
|
target?: string;
|
||||||
@@ -28,10 +32,13 @@ export type ColorConfig = Record<string, string>;
|
|||||||
export interface Config extends SectionConfig {
|
export interface Config extends SectionConfig {
|
||||||
services: Array<ServiceGroupConfig>;
|
services: Array<ServiceGroupConfig>;
|
||||||
|
|
||||||
|
columns: number;
|
||||||
colors: {
|
colors: {
|
||||||
light: ColorConfig;
|
light: ColorConfig;
|
||||||
dark: ColorConfig;
|
dark: ColorConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
links: Array<LinkConfig>;
|
||||||
|
|
||||||
[x: string]: unknown;
|
[x: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import configData from '../../config.yml';
|
import configData from '../../config.yml';
|
||||||
import { readFile, watch } from 'fs/promises';
|
import { readFile, watch } from 'fs/promises';
|
||||||
import * as yml from 'js-yaml';
|
import * as yml from 'js-yaml';
|
||||||
import type { ColorConfig, Config, ServiceConfig, ServiceGroupConfig } from '$lib/config';
|
import type {
|
||||||
|
ColorConfig,
|
||||||
|
Config,
|
||||||
|
LinkConfig,
|
||||||
|
ServiceConfig,
|
||||||
|
ServiceGroupConfig
|
||||||
|
} from '$lib/config';
|
||||||
|
|
||||||
const requiredService: Required<ServiceConfig> = {
|
const requiredService: Required<ServiceConfig> = {
|
||||||
title: '',
|
title: '',
|
||||||
@@ -23,16 +29,27 @@ const requiredServiceGroup: Required<ServiceGroupConfig> = {
|
|||||||
items: [requiredService]
|
items: [requiredService]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const requiredLink: Required<LinkConfig> = {
|
||||||
|
title: '',
|
||||||
|
subtitle: '',
|
||||||
|
logo: '',
|
||||||
|
icon: '',
|
||||||
|
asmask: false,
|
||||||
|
url: ''
|
||||||
|
};
|
||||||
|
|
||||||
export const requiredConfig: Required<Config> = {
|
export const requiredConfig: Required<Config> = {
|
||||||
title: '',
|
title: '',
|
||||||
subtitle: '',
|
subtitle: '',
|
||||||
logo: '',
|
logo: '',
|
||||||
icon: '',
|
icon: '',
|
||||||
asmask: false,
|
asmask: false,
|
||||||
|
columns: 3,
|
||||||
colors: {
|
colors: {
|
||||||
light: {},
|
light: {},
|
||||||
dark: {}
|
dark: {}
|
||||||
},
|
},
|
||||||
|
links: [requiredLink],
|
||||||
services: [requiredServiceGroup]
|
services: [requiredServiceGroup]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -95,6 +112,7 @@ defaultDarkConfig['background-image'] = '';
|
|||||||
|
|
||||||
export const defaultConfig: Config = {
|
export const defaultConfig: Config = {
|
||||||
title: 'Flanders',
|
title: 'Flanders',
|
||||||
|
columns: 3,
|
||||||
colors: {
|
colors: {
|
||||||
light: defaultLightConfig,
|
light: defaultLightConfig,
|
||||||
dark: defaultDarkConfig
|
dark: defaultDarkConfig
|
||||||
|
|||||||
6
src/lib/stores/layout.ts
Normal file
6
src/lib/stores/layout.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { persistentWritable } from '$lib/persistentStore';
|
||||||
|
import type { Writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export type LayoutDirection = 'column' | 'row';
|
||||||
|
|
||||||
|
export const layoutDirection: Writable<LayoutDirection> = persistentWritable('layout', 'column');
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { persistentWritable } from './persistentStore';
|
import { persistentWritable } from '$lib/persistentStore';
|
||||||
|
|
||||||
export type ThemeVariant = 'default' | 'dark' | 'light';
|
export type ThemeVariant = 'default' | 'dark' | 'light';
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import 'bulma/css/bulma.min.css';
|
import 'bulma/css/bulma.min.css';
|
||||||
import '@fortawesome/fontawesome-free/css/all.min.css';
|
import '@fortawesome/fontawesome-free/css/all.min.css';
|
||||||
|
|
||||||
import { themeVariant } from '$lib/themeVariant';
|
import { themeVariant } from '$lib/stores/themeVariant';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
$: {
|
$: {
|
||||||
if (browser) {
|
if (browser) {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Brand from '$lib/components/Brand.svelte';
|
import Brand from '$lib/components/Brand.svelte';
|
||||||
|
import Header from '$lib/components/Header.svelte';
|
||||||
import ServiceGroup from '$lib/components/ServiceGroup.svelte';
|
import ServiceGroup from '$lib/components/ServiceGroup.svelte';
|
||||||
import ThemeVariantButton from '$lib/components/ThemeVariantButton.svelte';
|
|
||||||
import type { ColorConfig } from '$lib/config';
|
import type { ColorConfig } from '$lib/config';
|
||||||
|
import { layoutDirection } from '$lib/stores/layout';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
function cssVariables(colors: ColorConfig): string {
|
function colorVariables(colors: ColorConfig): string {
|
||||||
const cssVars: Array<string> = [];
|
const cssVars: Array<string> = [];
|
||||||
for (const [key, value] of Object.entries(colors)) {
|
for (const [key, value] of Object.entries(colors)) {
|
||||||
cssVars.push(`--${key}:${value}`);
|
cssVars.push(`--${key}:${value}`);
|
||||||
@@ -15,24 +16,27 @@
|
|||||||
return cssVars.join(';');
|
return cssVars.join(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderColorVariables() {
|
function renderCssVariables() {
|
||||||
return `<style>
|
return `<style>
|
||||||
:root, body.is-light {
|
:root, body.is-light {
|
||||||
${cssVariables(data.config.colors.light)}
|
${colorVariables(data.config.colors.light)}
|
||||||
}
|
}
|
||||||
@media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {
|
@media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {
|
||||||
:root, body {
|
:root, body {
|
||||||
${cssVariables(data.config.colors.light)}
|
${colorVariables(data.config.colors.light)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
:root, body.is-dark {
|
:root, body.is-dark {
|
||||||
${cssVariables(data.config.colors.dark)}
|
${colorVariables(data.config.colors.dark)}
|
||||||
}
|
}
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root, body {
|
:root, body {
|
||||||
${cssVariables(data.config.colors.dark)}
|
${colorVariables(data.config.colors.dark)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
:root {
|
||||||
|
--layout-template: repeat(${data.config.columns},1fr);
|
||||||
|
}
|
||||||
</style>`;
|
</style>`;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -40,35 +44,17 @@
|
|||||||
<svelte:head>
|
<svelte:head>
|
||||||
<!-- it is safe here because config is comming from config.yml and thus is trusted. -->
|
<!-- it is safe here because config is comming from config.yml and thus is trusted. -->
|
||||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||||
{@html renderColorVariables()}
|
{@html renderCssVariables()}
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<header>
|
<Header section={data.config} links={data.config.links} />
|
||||||
<section class="first-line">
|
|
||||||
<div class="container">
|
|
||||||
<div class="logo">
|
|
||||||
<a href="/">
|
|
||||||
<Brand brand={data.config} color="var(--text-header)" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="dashboard-title">
|
|
||||||
{#if data.config.subtitle}
|
|
||||||
<span class="headline">{data.config.subtitle}</span>
|
|
||||||
{/if}
|
|
||||||
<h1>{data.config.title}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<nav class="navbar" aria-label="main navigation">
|
|
||||||
<div class="container">
|
|
||||||
<div class="navbar-start" />
|
|
||||||
<div class="navbar-end"><ThemeVariantButton /></div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="section">
|
<main class="section">
|
||||||
{#each data.config.services as group, i}
|
<div class="container layout-{$layoutDirection}">
|
||||||
<ServiceGroup {group} groupData={data.serviceData[i]} />
|
{#each data.config.services as group, i}
|
||||||
{/each}
|
<ServiceGroup {group} groupData={data.serviceData[i]} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<footer />
|
||||||
|
|||||||
Reference in New Issue
Block a user