Fix styling of landing page
This commit is contained in:
79
src/App.vue
79
src/App.vue
@@ -1,14 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink, RouterView } from 'vue-router'
|
||||
import WelcomeText from './components/WelcomeText.vue'
|
||||
import ThemeToggle from './components/ThemeToggle.vue'
|
||||
import LogoSvg from './components/icons/IconLogo.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ThemeToggle />
|
||||
<header>
|
||||
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
|
||||
|
||||
<div class="wrapper">
|
||||
<WelcomeText msg="Livonsaaren Tietokonepaja" />
|
||||
<div class="title-section">
|
||||
<div class="greetings">
|
||||
<div class="title-with-logo">
|
||||
<LogoSvg alt="Tietokonepajan logo" class="logo" />
|
||||
<h1>Livonsaaren Tietokonepaja</h1>
|
||||
</div>
|
||||
<h3>
|
||||
Tervetuloa Livonsaaren Tietokonepajan verkkosivuille! Tarjoamme kattavia IT-palveluita,
|
||||
laitehuoltoja, kotisivuratkaisuja ja Linux-tukea. Tutustu palveluihimme ja ota yhteyttä!
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<RouterLink to="/">Etusivu</RouterLink>
|
||||
@@ -26,9 +37,44 @@ header {
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.title-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.title-with-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
max-height: 120px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.greetings h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2.6rem;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.greetings h3 {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
nav {
|
||||
@@ -63,8 +109,27 @@ nav a:first-of-type {
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
.title-section {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.title-with-logo {
|
||||
justify-content: flex-start;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
max-height: 100px;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
|
||||
@@ -33,11 +33,50 @@
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
/* SVG/Icon colors */
|
||||
--color-svg-fill: #000000;
|
||||
--color-svg-stroke: #000000;
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
/* Dark theme - manual toggle */
|
||||
html.dark {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
|
||||
/* SVG/Icon colors for dark theme */
|
||||
--color-svg-fill: #ffffff;
|
||||
--color-svg-stroke: #ffffff;
|
||||
}
|
||||
|
||||
/* Light theme - manual toggle */
|
||||
html.light {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
/* SVG/Icon colors for light theme */
|
||||
--color-svg-fill: #000000;
|
||||
--color-svg-stroke: #000000;
|
||||
}
|
||||
|
||||
/* System preference fallback - only when no manual preference is set */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
html:not(.light):not(.dark) {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
@@ -47,6 +86,10 @@
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
|
||||
/* SVG/Icon colors for dark theme */
|
||||
--color-svg-fill: #ffffff;
|
||||
--color-svg-stroke: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,3 +127,36 @@ body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* Global SVG theming */
|
||||
svg {
|
||||
transition:
|
||||
fill 0.3s ease,
|
||||
stroke 0.3s ease;
|
||||
}
|
||||
|
||||
svg[fill='#000000'],
|
||||
svg[fill='#000'] {
|
||||
fill: var(--color-svg-fill) !important;
|
||||
}
|
||||
|
||||
svg[stroke='#000000'],
|
||||
svg[stroke='#000'] {
|
||||
stroke: var(--color-svg-stroke) !important;
|
||||
}
|
||||
|
||||
/* Target paths and other SVG elements with black fill/stroke */
|
||||
svg path[fill='#000000'],
|
||||
svg path[fill='#000'] {
|
||||
fill: var(--color-svg-fill) !important;
|
||||
}
|
||||
|
||||
svg path[stroke='#000000'],
|
||||
svg path[stroke='#000'] {
|
||||
stroke: var(--color-svg-stroke) !important;
|
||||
}
|
||||
|
||||
/* Target specific attribute values commonly used */
|
||||
svg g[fill='#000000'] {
|
||||
fill: var(--color-svg-fill) !important;
|
||||
}
|
||||
|
||||
128
src/components/ThemeToggle.vue
Normal file
128
src/components/ThemeToggle.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<button
|
||||
@click="toggleTheme"
|
||||
class="theme-toggle"
|
||||
:aria-label="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
|
||||
title="Toggle theme"
|
||||
>
|
||||
<svg v-if="isDark" class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<!-- Sun icon -->
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</svg>
|
||||
<svg v-else class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<!-- Moon icon -->
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
||||
</svg>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
|
||||
const isDark = ref(false)
|
||||
|
||||
const toggleTheme = () => {
|
||||
isDark.value = !isDark.value
|
||||
updateTheme()
|
||||
localStorage.setItem('theme', isDark.value ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
const updateTheme = () => {
|
||||
if (isDark.value) {
|
||||
document.documentElement.classList.add('dark')
|
||||
document.documentElement.classList.remove('light')
|
||||
} else {
|
||||
document.documentElement.classList.add('light')
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
}
|
||||
|
||||
const initTheme = () => {
|
||||
const savedTheme = localStorage.getItem('theme')
|
||||
if (savedTheme) {
|
||||
isDark.value = savedTheme === 'dark'
|
||||
} else {
|
||||
// Check system preference as fallback
|
||||
isDark.value = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
}
|
||||
updateTheme()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initTheme()
|
||||
})
|
||||
|
||||
// Watch for system theme changes when no preference is saved
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
mediaQuery.addEventListener('change', (e) => {
|
||||
if (!localStorage.getItem('theme')) {
|
||||
isDark.value = e.matches
|
||||
updateTheme()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.theme-toggle {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
background: var(--color-background-soft);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 50%;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background: var(--color-background-mute);
|
||||
border-color: var(--color-border-hover);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.theme-toggle:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--color-text);
|
||||
stroke-width: 2;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.theme-toggle:hover .icon {
|
||||
transform: rotate(15deg);
|
||||
}
|
||||
|
||||
/* Ensure the button works well on mobile */
|
||||
@media (max-width: 768px) {
|
||||
.theme-toggle {
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -23,3 +23,9 @@
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.st0 {
|
||||
fill: var(--color-svg-fill);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<svg
|
||||
fill="#000000"
|
||||
fill="var(--color-svg-fill)"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 1920 1920"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<svg
|
||||
fill="#000000"
|
||||
fill="var(--color-svg-fill)"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 32 32"
|
||||
|
||||
76
src/components/icons/IconLogo.vue
Normal file
76
src/components/icons/IconLogo.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="width"
|
||||
:height="height"
|
||||
viewBox="0 0 222.000000 222.000000"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
class="logo-svg"
|
||||
>
|
||||
<g
|
||||
transform="translate(0.000000,222.000000) scale(0.100000,-0.100000)"
|
||||
fill="var(--color-svg-fill)"
|
||||
stroke="none"
|
||||
>
|
||||
<path
|
||||
d="M0 1110 l0 -1110 1110 0 1110 0 0 1110 0 1110 -1110 0 -1110 0 0
|
||||
-1110z m2180 0 l0 -1070 -612 0 -613 0 222 222 c265 266 282 294 251 402 -10
|
||||
35 -66 96 -362 394 -193 195 -368 362 -388 373 -45 23 -115 25 -166 3 -24 -9
|
||||
-115 -93 -254 -232 l-218 -216 0 597 0 597 1070 0 1070 0 0 -1070z"
|
||||
/>
|
||||
<path
|
||||
d="M124 2086 c-16 -13 -17 -18 -6 -35 9 -17 10 -25 0 -40 -9 -15 -9 -23
|
||||
1 -35 11 -13 11 -19 0 -32 -10 -12 -10 -20 -1 -35 10 -15 9 -23 0 -39 -11 -17
|
||||
-10 -22 2 -30 12 -7 12 -12 0 -35 -12 -23 -12 -28 0 -35 12 -7 12 -12 0 -35
|
||||
-12 -23 -12 -28 0 -35 12 -7 12 -12 0 -35 -12 -23 -12 -28 0 -35 8 -6 103 -10
|
||||
210 -10 209 0 236 6 203 42 -14 16 -33 18 -143 18 -70 0 -130 3 -133 7 -4 3
|
||||
-1 13 6 21 9 12 9 16 0 19 -15 5 -17 39 -3 48 6 4 5 13 -3 25 -9 15 -9 25 -1
|
||||
38 8 12 8 22 0 35 -8 12 -8 22 0 35 8 12 8 22 0 35 -8 12 -8 22 0 34 8 13 8
|
||||
23 0 35 -8 13 -8 23 1 37 10 15 9 22 -6 35 -22 20 -100 21 -127 2z"
|
||||
/>
|
||||
<path
|
||||
d="M1199 1843 c-64 -32 -369 -325 -369 -355 0 -51 34 -29 193 130 200
|
||||
199 250 226 343 187 38 -16 355 -322 399 -385 15 -21 29 -56 32 -78 10 -76 -9
|
||||
-105 -190 -289 -167 -169 -190 -203 -139 -203 30 0 338 320 364 377 29 64 28
|
||||
135 -1 193 -30 59 -371 400 -428 429 -61 30 -135 28 -204 -6z"
|
||||
/>
|
||||
<path
|
||||
d="M1198 1571 c-56 -57 -98 -107 -98 -118 0 -25 40 -63 66 -63 19 0 224
|
||||
197 224 214 0 12 -22 51 -34 58 -34 23 -62 7 -158 -91z"
|
||||
/>
|
||||
<path
|
||||
d="M1479 1302 c-74 -73 -99 -104 -99 -123 0 -27 37 -69 60 -69 7 0 59
|
||||
46 116 102 110 108 122 130 84 168 -36 36 -57 25 -161 -78z"
|
||||
/>
|
||||
<path
|
||||
d="M1675 546 c-33 -24 -3 -46 60 -46 67 0 89 -8 75 -25 -8 -10 -8 -19 1
|
||||
-33 10 -16 10 -23 0 -35 -10 -12 -10 -19 0 -35 10 -16 10 -23 0 -35 -10 -12
|
||||
-10 -19 0 -35 10 -16 10 -23 0 -35 -10 -12 -10 -19 0 -35 10 -16 10 -23 0 -36
|
||||
-11 -13 -11 -19 0 -32 10 -13 10 -20 0 -36 -20 -32 2 -48 69 -48 67 0 89 16
|
||||
69 48 -10 16 -10 23 0 35 10 12 10 19 0 35 -10 16 -10 23 0 35 10 12 10 19 0
|
||||
35 -10 16 -10 23 0 35 10 12 10 19 0 35 -10 16 -10 23 0 35 10 12 10 19 0 35
|
||||
-10 16 -10 23 0 36 10 12 11 19 2 30 -16 19 5 26 74 26 63 0 93 22 60 46 -27
|
||||
20 -383 20 -410 0z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
width?: string | number
|
||||
height?: string | number
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
width: 125,
|
||||
height: 125,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.logo-svg {
|
||||
transition: fill 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
@@ -10,7 +10,7 @@
|
||||
>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000000"
|
||||
stroke="var(--color-svg-stroke)"
|
||||
stroke-width="2"
|
||||
stroke-miterlimit="10"
|
||||
d="M22.2,12.8l-3-3l5-5C23.3,4.3,22.2,4,21,4
|
||||
|
||||
Reference in New Issue
Block a user