Merge branch 'svelte-henri' into svelte

This commit is contained in:
Henri
2026-03-05 16:44:08 +02:00
23 changed files with 1916 additions and 327 deletions

3
.gitignore vendored
View File

@@ -22,3 +22,6 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
*storybook.log
storybook-static

11
.storybook/main.js Normal file
View File

@@ -0,0 +1,11 @@
/** @type { import('@storybook/svelte-vite').StorybookConfig } */
const config = {
stories: ['../src/**/*.stories.svelte'],
addons: ['@storybook/addon-svelte-csf'],
framework: {
name: '@storybook/svelte-vite',
options: {},
},
};
export default config;

13
.storybook/preview.js Normal file
View File

@@ -0,0 +1,13 @@
/** @type { import('@storybook/svelte-vite').Preview } */
const preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;

37
generate-stories.js Normal file
View File

@@ -0,0 +1,37 @@
import { readFileSync, writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const svelteSrc = readFileSync(resolve(__dirname, './src/components/Svg.svelte'), 'utf-8');
const match = svelteSrc.match(/export const VARIANTS\s*=\s*\[([^\]]+)\]/);
if (!match) {
console.error('Could not find VARIANTS in Svg.svelte');
process.exit(1);
}
const variants = match[1]
.split(',')
.map(v => v.trim().replace(/['"]/g, ''))
.filter(Boolean);
const stories = variants.map(v => `<Story name="${v}" args={{ type: '${v}' }} />`).join('\n');
const output = `<script context="module">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Svg from '../components/Svg.svelte';
const { Story } = defineMeta({
title: 'Components/Svg',
component: Svg,
});
</script>
${stories}
`;
const outPath = resolve(__dirname, './src/stories/Svg.stories.svelte');
writeFileSync(outPath, output, 'utf-8');
console.log(`✅ Generated ${variants.length} stories: ${variants.join(', ')}`);

1532
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,11 +6,18 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"prestorybook": "node generate-stories.js",
"prebuild-storybook": "node generate-stories.js"
}, },
"devDependencies": { "devDependencies": {
"@storybook/addon-svelte-csf": "^5.0.11",
"@storybook/svelte-vite": "^10.2.15",
"@sveltejs/vite-plugin-svelte": "^6.2.1", "@sveltejs/vite-plugin-svelte": "^6.2.1",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"storybook": "^10.2.15",
"svelte": "^5.43.8", "svelte": "^5.43.8",
"vite": "npm:rolldown-vite@7.2.5" "vite": "npm:rolldown-vite@7.2.5"
}, },

View File

@@ -23,11 +23,6 @@
url: https://taltech.ee/et/infotehnoloogia-teaduskond url: https://taltech.ee/et/infotehnoloogia-teaduskond
additionalText: additionalText:
- name: HexTech Solutions
image: hextech_logo.svg
url: https://hextech.ee/
additionalText:
- name: EAS & MKM - name: EAS & MKM
image: eas.svg image: eas.svg
url: https://eis.ee/ url: https://eis.ee/

View File

@@ -2,54 +2,42 @@
@import "tailwindcss"; @import "tailwindcss";
/* Overwriting tailwindcss specific variables */
@theme {
--color-orange-500: #f0941d;
--color-orange-300: #ffa940;
--color-orange-600: #d67d0a;
--color-gray-100: #fffdfa;
--color-gray-900: #0d0d0d;
--spacing-0: 0px;
--spacing-1: 4px;
--spacing-2: 8px;
--spacing-3: 16px;
--spacing-4: 24px;
--spacing-5: 32px;
--spacing-6: 48px;
--max-width-2xl: 700px;
--max-width-3xl: 800px;
--max-width-4xl: 900px;
}
/* Design tokens & theme variables (light defaults) */ /* Design tokens & theme variables (light defaults) */
:root { :root {
/* color tokens (light defaults) */
--orange: #f0941d;
--orange-light: #ffa940;
--orange-dark: #d67d0a;
--black: #000000;
--off-black: #0d0d0d;
--white: #ffffff;
--off-white: #fffdfa;
--gray-50: #fafafa;
--gray-100: #f5f5f5;
--gray-200: #e5e5e5;
--gray-300: #d4d4d4;
--gray-800: #262626;
--gray-900: #171717;
/* layout tokens */ /* layout tokens */
--site-padding: clamp(1rem, 2vw, 2rem); /* horizontal page padding */ --site-padding: clamp(1rem, 2vw, 2rem); /* horizontal page padding */
--content-max: 1200px; /* main content max width */ --content-max: 1200px; /* main content max width */
--nav-height: 64px;
--footer-height: 80px;
/* spacing scale */ /* spacing scale */
--space-0: 0px; --spacing-0: 0px;
--space-1: 4px; --spacing-1: 4px;
--space-2: 8px; --spacing-2: 8px;
--space-3: 16px; --spacing-3: 16px;
--space-4: 24px; --spacing-4: 24px;
--space-5: 32px; --spacing-5: 32px;
--space-6: 48px; --spacing-6: 48px;
/* radius / shadows */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 14px;
--radius-xl: 20px;
--shadow-1: 0 1px 2px rgba(240, 148, 29, 0.2),
0 1px 3px rgba(240, 148, 29, 0.12);
--shadow-2: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
--shadow-3: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
--shadow-glow: 0 0 20px rgba(240, 148, 29, 0.3);
/* breakpoints */
--bp-sm: 480px;
--bp-md: 768px;
--bp-lg: 1024px;
--bp-xl: 1280px;
/* type scale (fluid base + modular sizes) */ /* type scale (fluid base + modular sizes) */
--fs-base: clamp(0.95rem, 0.9vw + 0.9rem, 1rem); --fs-base: clamp(0.95rem, 0.9vw + 0.9rem, 1rem);
@@ -59,16 +47,6 @@
--fs-lg: 1.125rem; --fs-lg: 1.125rem;
--fs-xl: 1.5rem; --fs-xl: 1.5rem;
--line-height: 1.45; --line-height: 1.45;
/* derived component tokens that components can rely on */
--button-bg: var(--color-surface);
--button-text: var(--color-text);
--card-bg: linear-gradient(
180deg,
rgba(255, 255, 255, 0.98),
rgba(255, 255, 255, 0.96)
);
--accent: var(--color-primary);
} }
/* Global box sizing & accessible defaults */ /* Global box sizing & accessible defaults */
@@ -88,8 +66,6 @@ html {
} }
body { body {
margin: 0; margin: 0;
color: var(--color-text);
background: var(--color-bg);
line-height: var(--line-height); line-height: var(--line-height);
min-height: 100vh; min-height: 100vh;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
@@ -100,22 +76,18 @@ body {
/* Basic link & button defaults that components inherit */ /* Basic link & button defaults that components inherit */
a { a {
color: var(--color-primary);
text-decoration: none; text-decoration: none;
font-weight: 550; font-weight: 550;
} }
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
color: var(--color-primary-600);
} }
button, button,
input[type="button"], input[type="button"],
input[type="submit"] { input[type="submit"] {
font: inherit; font: inherit;
color: var(--button-text); border: 1px solid var(--color-border); /* TODO: fix css magic (we need the border, but the variable doesnt exist, but you cant remove it either) */
background: var(--button-bg);
border: 1px solid var(--color-border);
padding: 0.5em 0.9em; padding: 0.5em 0.9em;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
cursor: pointer; cursor: pointer;
@@ -126,17 +98,10 @@ button:active {
transform: translateY(1px); transform: translateY(1px);
} }
button:focus { button:focus {
outline: 3px solid color-mix(in srgb, var(--color-primary) 14%, transparent); outline: 3px solid color-mix(in srgb, var(--color-primary) 14%, transparent); /* TODO: change same magic here */
outline-offset: 2px; outline-offset: 2px;
} }
/* Simple responsive grid that adapts to available width */
.grid {
display: grid;
gap: var(--space-3);
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
/* Utility: visually hidden (accessible labels) */ /* Utility: visually hidden (accessible labels) */
.sr-only, .sr-only,
.visually-hidden { .visually-hidden {
@@ -167,49 +132,34 @@ button:focus {
} }
} }
/* Navbar & footer helpers so components can assume consistent heights */ /* Accessibility settings */
.site-header {
height: var(--nav-height);
display: flex;
align-items: center;
background: transparent;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 40;
border-bottom: 1px solid var(--color-border);
backdrop-filter: blur(6px);
}
.site-footer {
height: var(--footer-height);
display: flex;
align-items: center;
border-top: 1px solid var(--color-border);
background: transparent;
}
/* Small form controls default */
.input {
padding: 0.6em 0.8em;
border-radius: var(--radius-sm);
border: 1px solid var(--color-border);
background: transparent;
color: inherit;
}
/* Utility to visually reduce emphasis (muted text) */
.muted {
color: var(--color-muted);
font-size: 0.95em;
}
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
* { * {
animation-duration: 0.001ms !important; animation: none !important;
animation-iteration-count: 1 !important; transition: none !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important; scroll-behavior: auto !important;
} }
/* Automatically apply final animation states */
[data-animation] { /* Everything with animation needs to be marked like this: <div class="fade-in" data-animation>Hello World</div> */
all: unset !important;
}
}
@media (prefers-reduced-transparency: reduce) {
* {
background: solid !important;
backdrop-filter: none !important;
}
}
@media (forced-colors: active) {
* {
background: Window !important;
color: WindowText !important;
border-color: WindowText !important;
}
a {
color: Highlight !important;
}
} }

View File

@@ -1,23 +1,10 @@
<script> <script>
export let onClick = () => {}; export let onClick = () => {};
export let buttonHoverStyle = "";
export let style = "";
let isHovered = false;
</script> </script>
<button <button
on:click={onClick} on:click={onClick}
on:mouseenter={() => (isHovered = true)} class="btn border-[1.5px] px-5 py-2.5 font-medium cursor-pointer flex items-center gap-2 whitespace-nowrap {$$props.class ?? ''}"
on:mouseleave={() => (isHovered = false)}
class={`btn border-[1.5px] px-5 py-2.5 font-medium cursor-pointer flex items-center gap-2 whitespace-nowrap ${$$restProps.class}`}
style={isHovered && buttonHoverStyle ? buttonHoverStyle : style}
> >
<slot /> <slot />
</button> </button>
<style>
.btn {
transition: background-color 0.2s ease, color 0.2s ease;
}
</style>

View File

@@ -4,7 +4,6 @@
export let name = ""; export let name = "";
export let buttonClass = ""; export let buttonClass = "";
export let panelClassName = ""; export let panelClassName = "";
export let buttonHoverStyle = "";
let isOpen = false; let isOpen = false;
@@ -22,7 +21,7 @@
</script> </script>
<div class="relative inline-block" on:focusout={handleFocusOut}> <div class="relative inline-block" on:focusout={handleFocusOut}>
<Button onClick={toggle} class={buttonClass} {buttonHoverStyle}> <Button onClick={toggle} class={buttonClass}>
{name} {name}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -1,10 +1,11 @@
<!-- <!--
Multi-purpose image/SVG component with optional link wrapper, Multi-purpose image component with optional link wrapper,
hover effects, size variants, and object-fit options. hover effects, size variants, and object-fit options. Includes support for WebP format with fallback to standard formats.
--> -->
<script> <script>
export let src = ""; export let webpSrc = ""; // Optional WebP source for browsers that support it
export let src = ""; // Fallback (some devices don't support WebP) or primary source if webpSrc is not provided
export let alt = ""; export let alt = "";
export let href = null; // optional link URL export let href = null; // optional link URL
export let target = "_blank"; export let target = "_blank";
@@ -13,6 +14,12 @@ hover effects, size variants, and object-fit options.
export let hover = false; export let hover = false;
export let className = ""; export let className = "";
// optional srcset for responsive images (w descriptors means width-based, x descriptors means pixel density-based)
// e.g. "image-400.jpg 400w, image-800.jpg 800w"
export let srcSet = ""; // always set src if using this
export let webpSrcSet = ""; // always set src if using this
export let pictureClass = ""; // CSS/Tailwind classes applied to the <picture> element
$: objectFitClass = { $: objectFitClass = {
contain: "object-contain", contain: "object-contain",
cover: "object-cover", cover: "object-cover",
@@ -25,22 +32,29 @@ hover effects, size variants, and object-fit options.
</script> </script>
{#if href} {#if href}
<a <a {href} {target} {rel} class="inline-block leading-none">
{href} <picture class={pictureClass}>
{target} {#if webpSrc || webpSrcSet}
{rel} <source srcset={webpSrcSet || webpSrc} type="image/webp" />
class="inline-block leading-none" {/if}
>
<img <img
{src} {src}
{alt} srcset={srcSet || undefined}
alt={alt}
class="block h-auto max-w-full {objectFitClass} {hoverClasses} {className}" class="block h-auto max-w-full {objectFitClass} {hoverClasses} {className}"
/> />
</picture>
</a> </a>
{:else} {:else}
<picture class={pictureClass}>
{#if webpSrc || webpSrcSet}
<source srcset={webpSrcSet || webpSrc} type="image/webp" />
{/if}
<img <img
{src} {src}
{alt} srcset={srcSet || undefined}
alt={alt}
class="block h-auto max-w-full {objectFitClass} {hoverClasses} {className}" class="block h-auto max-w-full {objectFitClass} {hoverClasses} {className}"
/> />
</picture>
{/if} {/if}

26
src/components/README.md Normal file
View File

@@ -0,0 +1,26 @@
# SVG previews
use `npm run storybook` which opens a browser window with all the SVG variations. You can also edit the SVG properties in storybook controls.
# Components
under components are building blocks that are not layout related.
## details
- Button: a simple button component with some styling.
- Dropdown: a dropdown component that can be used to show/hide content.
- Slideshow: a simple slideshow component that can be used to show multiple images in a slideshow format. Includes options to enable/disable autoplay and to set the autoplay interval.
- Svg: a component that renders different SVG icons based on the `type` prop. See the VARIANTS constant in `Svg.svelte` or storybook for the available types.
- Image (Use for larger images): a component that renders an image with some styling. It accepts a lot of props. Main ones are `src` (the main image source that is used if anything else fails or not provided), `alt` (the alt text for the image), `srcSet` (a string of comma-separated image sources for different screen sizes, jpg or png files), `webpSrc` (the webp image source as a webp file), `webpSrcSet` (a string of comma-separated webp image sources for different screen sizes). We use 2 webp images: 400 and 800 width, and one jpg image 800 width as default src for fallback.
# Layout components
under components/layout are components that are used to structure the page, such as Container, Stack, Center, etc.
- Container: a simple container component that centers its content and adds some padding.
- Stack: a component that stacks its children vertically or horizontally.
- Center: a component that centers its content both horizontally and vertically.
- Section: a component that is used to create sections on the page.
- Card: a component that adds some padding, a border, and a shadow to its content, used to create cards on the page.
- Grid: a component that creates a CSS grid layout for its children, with customizable columns and gap. It is dynamic and changes automatically with page size, best practice is to set `columns` for desktop and `mobileColumns` for mobile views to control the behavior if needed.

75
src/components/Svg.svelte Normal file
View File

@@ -0,0 +1,75 @@
<script context="module">
export const VARIANTS = ['instagram','facebook','github','connector','mirrorV','mirrorH','mirrorVH','branch','branchH'];
</script>
<script>
export let type;
export let className = "";
</script>
{#if type === "connector"}
<svg viewBox="0 0 182 66" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 63.5H67.5L102.5 15.5H156" stroke="currentColor" stroke-width="5"/>
<circle cx="166" cy="15.5" r="10" stroke="currentColor" stroke-width="5"/>
</svg>
{:else if type === "mirrorV"}
<svg viewBox="0 0 182 66" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="scale(1,-1) translate(0,-66)">
<path d="M0 63.5H67.5L102.5 15.5H156" stroke="currentColor" stroke-width="5"/>
<circle cx="166" cy="15.5" r="10" stroke="currentColor" stroke-width="5"/>
</g>
</svg>
{:else if type === "mirrorH"}
<svg viewBox="0 0 182 66" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="scale(-1,1) translate(-182,0)">
<path d="M0 63.5H67.5L102.5 15.5H156" stroke="currentColor" stroke-width="5"/>
<circle cx="166" cy="15.5" r="10" stroke="currentColor" stroke-width="5"/>
</g>
</svg>
{:else if type === "mirrorVH"}
<svg viewBox="0 0 182 66" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="scale(-1,-1) translate(-182,-66)">
<path d="M0 63.5H67.5L102.5 15.5H156" stroke="currentColor" stroke-width="5"/>
<circle cx="166" cy="15.5" r="10" stroke="currentColor" stroke-width="5"/>
</g>
</svg>
{:else if type === "branch"}
<svg viewBox="0 0 75 74" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 31.4784H46L68 1.47842M45.5 30.9784L66.5 63.9784"
stroke="currentColor"
stroke-width="5"/>
</svg>
{:else if type === "branchH"}
<svg viewBox="0 0 75 74" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="scale(-1,1) translate(-75,0)">
<path d="M0 31.4784H46L68 1.47842M45.5 30.9784L66.5 63.9784"
stroke="currentColor"
stroke-width="5"/>
</g>
</svg>
{:else if type === "instagram"}
<svg class="w-8 h-8 {className}" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/>
</svg>
{:else if type === "facebook"}
<svg class="w-8 h-8 {className}" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
</svg>
{:else if type === "github"}
<svg class="w-8 h-8 {className}" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
{:else}
<div>Unknown type: {type}</div>
{/if}

View File

@@ -1,3 +1,4 @@
// layout components
export { default as Container } from './layout/Container.svelte' export { default as Container } from './layout/Container.svelte'
export { default as Card } from './layout/Card.svelte' export { default as Card } from './layout/Card.svelte'
export { default as Stack } from './layout/Stack.svelte' export { default as Stack } from './layout/Stack.svelte'
@@ -5,6 +6,9 @@ export { default as Grid } from './layout/Grid.svelte'
export { default as Center } from './layout/Center.svelte' export { default as Center } from './layout/Center.svelte'
export { default as Section } from './layout/Section.svelte' export { default as Section } from './layout/Section.svelte'
// components
export { default as Button } from './Button.svelte' export { default as Button } from './Button.svelte'
export { default as Dropdown } from './Dropdown.svelte' export { default as Dropdown } from './Dropdown.svelte'
export { default as Slideshow } from './Slideshow.svelte' export { default as Slideshow } from './Slideshow.svelte'
export { default as Svg } from './Svg.svelte'
export { default as Image } from './Image.svelte'

View File

@@ -1,6 +1,6 @@
<script> <script>
export let dir = "row"; // 'row' | 'col' (default: 'row') export let dir = "row"; // 'row' | 'col' (default: 'row')
export let gapClass = "gap-4"; export let gap = "gap-4";
export let className = ""; export let className = "";
</script> </script>
@@ -8,7 +8,7 @@
class={"flex " + class={"flex " +
(dir === "row" ? "flex-row" : "flex-col") + (dir === "row" ? "flex-row" : "flex-col") +
" items-center justify-center " + " items-center justify-center " +
gapClass + gap +
" " + " " +
className} className}
> >

View File

@@ -1,34 +1,40 @@
<!--
A responsive grid wrapper using CSS Grid. It exposes `min`, `columns`,
and `gap` props to control the responsive column minimum width, fixed
column count, and spacing. Content is projected into the default slot.
-->
<script> <script>
export let min = "220px"; // minimum column width (used when columns is not set) export let min = "220px"; // min column width (used when columns not set)
export let columns = null; // fixed number of columns (e.g., "2", "3", "4") export let columns = null; // fixed number of columns for desktop
export let gap = "var(--space-3)"; export let mobileColumns = null; // fixed number of columns for mobile
export let gap = "gap-3";
export let className = ""; export let className = "";
$: gridTemplateColumns = columns $: desktopColumns = columns ? `repeat(${columns}, 1fr)` : '1fr';
? `repeat(${columns}, 1fr)` $: mobileCols = mobileColumns ? `repeat(${mobileColumns}, 1fr)` : '1fr';
: '1fr';
</script> </script>
<div <div
class="grid {className}" class="grid {className} {gap}"
style="--min: {min}; --gap: {gap}; --columns: {gridTemplateColumns}; grid-template-columns: var(--columns); gap: var(--gap);" style="
--min: {min};
--desktop-columns: {desktopColumns};
--mobile-columns: {mobileCols};
grid-template-columns: var(--mobile-columns);
"
> >
<slot /> <slot />
</div> </div>
<style> <style>
@media (min-width: 768px) { /* Mobile first: use mobileColumns by default */
.grid { .grid {
grid-template-columns: var(--columns) !important; grid-template-columns: var(--mobile-columns);
} }
.grid:not([style*="--columns: repeat"]) { /* Desktop / tablet screens */
@media (min-width: 768px) {
.grid {
grid-template-columns: var(--desktop-columns) !important;
}
/* fallback for when columns are not set, use minmax */
.grid:not([style*="--desktop-columns: repeat"]) {
grid-template-columns: repeat(auto-fit, minmax(var(--min), 1fr)) !important; grid-template-columns: repeat(auto-fit, minmax(var(--min), 1fr)) !important;
} }
} }

View File

@@ -1,33 +1,22 @@
<script> <script>
export let background = "transparent"; // CSS color code or color variable name from app.css export let bg = "";
export let padding = "normal"; // 'none' | 'small' | 'normal' | 'large' export let padding = "normal";
export let fullWidth = false; export let fullWidth = false;
export let id = ""; export let id = "";
export let className = ""; export let className = "";
const paddingClasses = { const paddingClasses = {
none: "py-0", none: "py-0",
tight: "py-2 md:py-3",
small: "py-4 md:py-8", small: "py-4 md:py-8",
normal: "py-12 max-md:py-8", normal: "py-12 max-md:py-8",
large: "py-[clamp(3rem,8vw,6rem)] max-md:py-12", large: "py-[clamp(3rem,8vw,6rem)] max-md:py-12",
}; };
// Convert background to appropriate CSS value
$: backgroundStyle = (() => {
if (!background || background === "transparent") return "";
// If it starts with # or rgb/rgba or hsl, it's a direct color
if (background.startsWith("#") || background.startsWith("rgb") || background.startsWith("hsl")) {
return background;
}
// Otherwise treat as CSS variable name
return `var(--${background})`;
})();
</script> </script>
<section <section
{id} {id}
class="w-full {paddingClasses[padding]} {className}" class="w-full {paddingClasses[padding]} {bg} {className}"
style={backgroundStyle ? `background: ${backgroundStyle};` : ""}
> >
<div class="{fullWidth ? '' : 'max-w-[1200px]'} mx-auto px-(--site-padding)"> <div class="{fullWidth ? '' : 'max-w-[1200px]'} mx-auto px-(--site-padding)">
<slot /> <slot />

View File

@@ -3,11 +3,11 @@ A simple stack/column layout that stacks children vertically.
--> -->
<script> <script>
export let gap = "var(--space-3)"; export let gap = "gap-3";
export let align = "stretch"; export let align = "stretch";
export let className = ""; export let className = "";
</script> </script>
<div class="flex flex-col {className}" style="gap: {gap}; align-items: {align};"> <div class="flex flex-col {className} {gap}" style="align-items: {align};">
<slot /> <slot />
</div> </div>

View File

@@ -4,26 +4,27 @@ import {
Section, Section,
Container, Container,
Center, Center,
Svg,
} from "$components"; } from "$components";
</script> </script>
<Section background="off-black" className="text-white"> <Section bg="bg-gray-900" className="text-white">
<Grid> <Grid>
<div> <div>
<h3 class="text-3xl font-light" style="padding-bottom: var(--space-4);">MTÜ Lapikud</h3> <h3 class="text-3xl font-light pb-4">MTÜ Lapikud</h3>
<Container center={false}> <Container center={false}>
<address class="not-italic" style="padding-bottom: var(--space-4);"> <address class="not-italic pb-4">
Aadress: <a href="https://maps.app.goo.gl/1pCDMVNaMD29G7y78" style="color: var(--orange)">Akadeemia tee 5, 12616 Tallinn, Eesti</a> Aadress: <a href="https://maps.app.goo.gl/1pCDMVNaMD29G7y78" class="text-orange-500">Akadeemia tee 5, 12616 Tallinn, Eesti</a>
<br/> <br/>
E-post: <a href="mailto:lapikud@lapikud.ee" style="color: var(--orange)">lapikud@lapikud.ee</a> E-post: <a href="mailto:lapikud@lapikud.ee" class="text-orange-500">lapikud@lapikud.ee</a>
<br/> <br/>
Tel: <a href="tel:+37258160799" style="color: var(--orange)">+372 58 160 799</a> Tel: <a href="tel:+37258160799" class="text-orange-500">+372 58 160 799</a>
<br/> <br/>
Messenger: <a href="https://m.me/Lapikud" target="_blank" rel="noopener noreferrer" style="color: var(--orange)">m.me/Lapikud</a> Messenger: <a href="https://m.me/Lapikud" target="_blank" rel="noopener noreferrer" class="text-orange-500">m.me/Lapikud</a>
</address> </address>
<div> <div>
Reg. kood: <a href="https://ariregister.rik.ee/est/company/80167145/" style="color: var(--orange)">80167145</a> Reg. kood: <a href="https://ariregister.rik.ee/est/company/80167145/" class="text-orange-500">80167145</a>
<br/> <br/>
Swedbank EE812200221019551756 Swedbank EE812200221019551756
</div> </div>
@@ -31,26 +32,20 @@ import {
</div> </div>
<div> <div>
<Container> <Container>
<h3 class="text-3xl font-light flex justify-center" style="padding-bottom: var(--space-4);">Sotsiaalmeedia</h3> <h3 class="text-3xl font-light flex justify-center pb-4">Sotsiaalmeedia</h3>
<div class="flex items-center justify-center" style="gap: var(--space-4);"> <div class="flex items-center justify-center gap-4">
<a href="https://www.instagram.com/lapikud/" target="_blank" rel="noopener noreferrer" aria-label="Instagram" class="transition-opacity duration-200"> <a href="https://www.instagram.com/lapikud/" target="_blank" rel="noopener noreferrer" aria-label="Instagram" class="transition-opacity duration-200">
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> <Svg type="instagram"/>
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/>
</svg>
</a> </a>
<a href="https://www.facebook.com/Lapikud" target="_blank" rel="noopener noreferrer" aria-label="Facebook" class="transition-opacity duration-200"> <a href="https://www.facebook.com/Lapikud" target="_blank" rel="noopener noreferrer" aria-label="Facebook" class="transition-opacity duration-200">
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> <Svg type="facebook"/>
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
</svg>
</a> </a>
<a href="https://github.com/lapikud" target="_blank" rel="noopener noreferrer" aria-label="GitHub" class="transition-opacity duration-200"> <a href="https://github.com/lapikud" target="_blank" rel="noopener noreferrer" aria-label="GitHub" class="transition-opacity duration-200">
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> <Svg type="github"/>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
</a> </a>
</div> </div>
</Container> </Container>
</div> </div>
</Grid> </Grid>
<Center><small style="color: var(--orange); padding-top: var(--space-6);">© 2026 MTÜ Lapikud</small></Center> <Center><small class="text-orange-500 pt-6">© 2026 MTÜ Lapikud</small></Center>
</Section> </Section>

View File

@@ -105,9 +105,9 @@
} }
</style> </style>
<Section className="text-black" background="orange" padding="small"> <Section className="text-black" bg="bg-orange-500" padding="tight">
{#if $text && Object.keys($text).length > 0} {#if $text && Object.keys($text).length > 0}
<div class="flex items-center w-full relative min-h-16"> <div class="flex items-center w-full relative min-h-12">
<!-- Logo --> <!-- Logo -->
{#if currentPath !== "/"} {#if currentPath !== "/"}
<div class="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 md:relative md:left-auto md:translate-x-0 md:top-auto md:translate-y-0"> <div class="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 md:relative md:left-auto md:translate-x-0 md:top-auto md:translate-y-0">
@@ -122,50 +122,47 @@
<nav class="z-10 hidden md:flex gap-4 justify-end text-xl"> <nav class="z-10 hidden md:flex gap-4 justify-end text-xl">
<Dropdown <Dropdown
name={$text.about || 'Meist'} name={$text.about || 'Meist'}
buttonClass="rounded-[5px]" buttonClass="rounded-[5px] hover:text-white"
buttonHoverStyle="color:var(--white)" panelClassName="bg-white text-black"
panelClassName="bg-[var(--white)] text-[var(--black)]"
> >
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/lapikutest")} <Button class="rounded-[5px] bg-transparent hover:text-orange-500" onClick={() => navigate("/lapikutest")}
>{$text.aboutPages.info}</Button >{$text.aboutPages.info}</Button
> >
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/tudengile")} <Button class="rounded-[5px] bg-transparent hover:text-orange-500" onClick={() => navigate("/tudengile")}
><Bot />{$text.aboutPages.join}</Button ><Bot />{$text.aboutPages.join}</Button
> >
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/mentorid")} <Button class="rounded-[5px] bg-transparent hover:text-orange-500" onClick={() => navigate("/mentorid")}
><HandHeart />{$text.aboutPages.mentors}</Button ><HandHeart />{$text.aboutPages.mentors}</Button
> >
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/juhatus")} <Button class="rounded-[5px] bg-transparent hover:text-orange-500" onClick={() => navigate("/juhatus")}
><Lectern />{$text.aboutPages.board}</Button ><Lectern />{$text.aboutPages.board}</Button
> >
</Dropdown> </Dropdown>
<Dropdown <Dropdown
name={$text.events} name={$text.events}
buttonClass="rounded-[5px]" buttonClass="rounded-[5px] hover:text-white"
buttonHoverStyle="color:var(--white)" panelClassName="bg-white text-black"
panelClassName="bg-[var(--white)] text-[var(--black)]"
> >
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/koolitused")} <Button class="rounded-[5px] bg-transparent hover:text-orange-500" onClick={() => navigate("/koolitused")}
><Presentation />{$text.eventsPages.workshops}</Button ><Presentation />{$text.eventsPages.workshops}</Button
> >
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("https://asikarikas.ee/", { external: true })} <Button class="rounded-[5px] bg-transparent hover:text-orange-500" onClick={() => navigate("https://asikarikas.ee/", { external: true })}
><Trophy />ASI Karikas</Button ><Trophy />ASI Karikas</Button
> >
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("https://remondikohvik.lapikud.ee/", { external: true })} <Button class="rounded-[5px] bg-transparent hover:text-orange-500" onClick={() => navigate("https://remondikohvik.lapikud.ee/", { external: true })}
><Coffee />{$text.eventsPages.repair}</Button ><Coffee />{$text.eventsPages.repair}</Button
> >
</Dropdown> </Dropdown>
<Button buttonHoverStyle="color:var(--white)" class="rounded-[5px]" onClick={() => navigate("/kontakt")} <Button class="rounded-[5px] hover:text-white" onClick={() => navigate("/kontakt")}
>{$text.contact} >{$text.contact}
</Button> </Button>
<Button buttonHoverStyle="color:var(--white)" class="rounded-[5px]" onClick={() => navigate("/helpdesk")} <Button class="rounded-[5px] hover:text-white" onClick={() => navigate("/helpdesk")}
>{$text.helpdesk} >{$text.helpdesk}
</Button> </Button>
<!-- Language Switcher --> <!-- Language Switcher -->
<Button <Button
class="rounded-[5px] px-3 py-1 text-sm ml-2" class="rounded-[5px] px-3 py-1 text-sm ml-2 hover:text-white"
buttonHoverStyle="color:var(--white)"
onClick={() => switchLanguageRoute($currentLang === 'est' ? 'en' : 'est')} onClick={() => switchLanguageRoute($currentLang === 'est' ? 'en' : 'est')}
> >
{$currentLang === 'est' ? 'EN' : 'EST'} {$currentLang === 'est' ? 'EN' : 'EST'}
@@ -175,8 +172,7 @@
<!-- Mobile Hamburger Button - Fixed position --> <!-- Mobile Hamburger Button - Fixed position -->
<Button <Button
onClick={toggleMobileMenu} onClick={toggleMobileMenu}
class="md:hidden p-2 text-white transition-colors bg-transparent absolute right-0" class="md:hidden p-2 text-white transition-colors bg-transparent absolute right-0 hover:text-white"
buttonHoverStyle="color: var(--white)"
style="opacity: {mobileMenuOpen ? '0' : '1'}; pointer-events: {mobileMenuOpen ? 'none' : 'auto'};" style="opacity: {mobileMenuOpen ? '0' : '1'}; pointer-events: {mobileMenuOpen ? 'none' : 'auto'};"
aria-label="Toggle menu" aria-label="Toggle menu"
> >
@@ -198,8 +194,7 @@
<div class="absolute top-8 right-0 px-(--site-padding)"> <div class="absolute top-8 right-0 px-(--site-padding)">
<Button <Button
onClick={closeMobileMenu} onClick={closeMobileMenu}
class="ml-auto p-2 transition-colors flex bg-transparent" class="ml-auto p-2 transition-colors flex bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
aria-label="Close menu" aria-label="Close menu"
> >
<X size={32} color="white" /> <X size={32} color="white" />
@@ -207,38 +202,34 @@
</div> </div>
<!-- Menu Content --> <!-- Menu Content -->
<Stack gap="var(--space-5)" className="flex-1 px-8 pt-24 pb-12 text-white text-xl"> <Stack gap="gap-5" className="flex-1 px-8 pt-24 pb-12 text-white text-xl">
<!-- About Section --> <!-- About Section -->
<Stack gap="var(--space-3)"> <Stack gap="gap-3">
<h3 class="text-2xl font-bold" style="color: var(--orange)">{$text.about}</h3> <h3 class="text-2xl font-bold text-orange-500">{$text.about}</h3>
<Stack gap="var(--space-2)" className="pl-4"> <Stack gap="gap-2" className="pl-4">
<Button <Button
onClick={() => handleNavigation("/lapikutest")} onClick={() => handleNavigation("/lapikutest")}
class="text-left transition-colors flex items-center gap-2 bg-transparent" class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
{$text.aboutPages.info} {$text.aboutPages.info}
</Button> </Button>
<Button <Button
onClick={() => handleNavigation("/tudengile")} onClick={() => handleNavigation("/tudengile")}
class="text-left transition-colors flex items-center gap-2 bg-transparent" class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
<Bot size={20} /> <Bot size={20} />
{$text.aboutPages.join} {$text.aboutPages.join}
</Button> </Button>
<Button <Button
onClick={() => handleNavigation("/mentorid")} onClick={() => handleNavigation("/mentorid")}
class="text-left transition-colors flex items-center gap-2 bg-transparent" class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
<HandHeart size={20} /> <HandHeart size={20} />
{$text.aboutPages.mentors} {$text.aboutPages.mentors}
</Button> </Button>
<Button <Button
onClick={() => handleNavigation("/juhatus")} onClick={() => handleNavigation("/juhatus")}
class="text-left transition-colors flex items-center gap-2 bg-transparent" class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
<Lectern size={20} /> <Lectern size={20} />
{$text.aboutPages.board} {$text.aboutPages.board}
@@ -247,29 +238,26 @@
</Stack> </Stack>
<!-- Events Section --> <!-- Events Section -->
<Stack gap="var(--space-3)" > <Stack gap="gap-3" >
<h3 class="text-2xl font-bold" style="color: var(--orange)">{$text.events}</h3> <h3 class="text-2xl font-bold text-orange-500">{$text.events}</h3>
<Stack gap="var(--space-2)" className="pl-4"> <Stack gap="gap-2" className="pl-4">
<Button <Button
onClick={() => handleNavigation("/mentorid")} onClick={() => handleNavigation("/mentorid")}
class="text-left transition-colors flex items-center gap-2 bg-transparent" class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
<Presentation size={20} /> <Presentation size={20} />
{$text.eventsPages.workshops} {$text.eventsPages.workshops}
</Button> </Button>
<Button <Button
onClick={() => handleNavigation("https://asikarikas.ee/", { external: true })} onClick={() => handleNavigation("https://asikarikas.ee/", { external: true })}
class="text-left transition-colors flex items-center gap-2 bg-transparent" class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
<Trophy size={20} /> <Trophy size={20} />
ASI Karikas ASI Karikas
</Button> </Button>
<Button <Button
onClick={() => handleNavigation("https://remondikohvik.lapikud.ee/", { external: true })} onClick={() => handleNavigation("https://remondikohvik.lapikud.ee/", { external: true })}
class="text-left transition-colors flex items-center gap-2 bg-transparent" class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
<Coffee size={20} /> <Coffee size={20} />
{$text.eventsPages.repair} {$text.eventsPages.repair}
@@ -278,27 +266,24 @@
</Stack> </Stack>
<!-- Direct Links --> <!-- Direct Links -->
<Stack gap="var(--space-3)"> <Stack gap="gap-3">
<Button <Button
onClick={() => handleNavigation("/kontakt")} onClick={() => handleNavigation("/kontakt")}
class="text-left text-2xl font-semibold transition-colors bg-transparent" class="text-left text-2xl font-semibold transition-colors bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
{$text.contact} {$text.contact}
</Button> </Button>
<Button <Button
onClick={() => handleNavigation("/helpdesk")} onClick={() => handleNavigation("/helpdesk")}
class="text-left text-2xl font-semibold transition-colors bg-transparent" class="text-left text-2xl font-semibold transition-colors bg-transparent hover:text-orange-500"
buttonHoverStyle="color: var(--orange)"
> >
{$text.helpdesk} {$text.helpdesk}
</Button> </Button>
</Stack> </Stack>
<!-- Language Switcher --> <!-- Language Switcher -->
<Stack gap="var(--space-3)"> <Stack gap="gap-3">
<Button <Button
class="w-full rounded-lg py-3 text-lg" class="w-full rounded-lg py-3 text-lg bg-orange-500 text-black"
style="background-color: var(--orange); color: var(--black)"
onClick={() => handleLanguageSwitch($currentLang === 'est' ? 'en' : 'est')} onClick={() => handleLanguageSwitch($currentLang === 'est' ? 'en' : 'est')}
> >
{$currentLang === 'est' ? 'English' : 'Eesti'} {$currentLang === 'est' ? 'English' : 'Eesti'}

View File

@@ -25,12 +25,12 @@
</script> </script>
<!-- Header Section --> <!-- Header Section -->
<Section background="orange"> <Section bg="bg-orange-500">
<Grid> <Grid>
<Center> <Center>
<img src="/assets/LapLogo_black_white.png" alt="MTÜ Lapikud" class="max-w-md w-full h-auto" style="padding: var(--space-4);"/> <img src="/assets/LapLogo_black_white.png" alt="MTÜ Lapikud" class="max-w-md w-full h-auto p-4"/>
</Center> </Center>
<p class="text-3xl leading-relaxed" style="padding: var(--space-4);"> <p class="text-3xl leading-relaxe p-4">
MTÜ Lapikud on Tallinna Tehnikaülikooli Tarkvaraarendusklubi, mis ühendab endisi MTÜ Lapikud on Tallinna Tehnikaülikooli Tarkvaraarendusklubi, mis ühendab endisi
ja praegusi IT huvilisi tudengeid. ja praegusi IT huvilisi tudengeid.
</p> </p>
@@ -38,42 +38,42 @@
</Section> </Section>
<Section> <Section>
<Grid gap="var(--space-5)"> <Grid gap="gap-5">
<Card variant="blur" href="/tudengile"> <Card variant="blur" href="/tudengile">
<div class="feature-icon" style="color: var(--orange)"> <div class="feature-icon text-orange-500">
<Users size={32} strokeWidth={1.5} /> <Users size={32} strokeWidth={1.5} />
</div> </div>
<h3 class="feature-title">Tudengile</h3> <h3 class="feature-title">Tudengile</h3>
<p class="feature-description"> <p class="feature-description">
Tule arenda oma oskuseid ja saa ägedaid sõpru! Omanda praktilist kogemust reaalsetest projektidest. Tule arenda oma oskuseid ja saa ägedaid sõpru! Omanda praktilist kogemust reaalsetest projektidest.
</p> </p>
<div style="color: var(--orange)" class="mt-4 font-semibold flex items-center gap-2"> <div class="mt-4 font-semibold flex items-center gap-2 text-orange-500">
Liitu meiega <ArrowRight size={16} /> Liitu meiega <ArrowRight size={16} />
</div> </div>
</Card> </Card>
<Card variant="blur" href="/helpdesk"> <Card variant="blur" href="/helpdesk">
<div class="feature-icon" style="color: var(--orange)"> <div class="feature-icon text-orange-500">
<Wrench size={32} strokeWidth={1.5} /> <Wrench size={32} strokeWidth={1.5} />
</div> </div>
<h3 class="feature-title">Helpdesk</h3> <h3 class="feature-title">Helpdesk</h3>
<p class="feature-description"> <p class="feature-description">
HELPDESK on MTÜ Lapikute poolt pakutav arvutiabiteenus. Teenus on suunatud tudengitele, õppejõududele ning kõikidele huvilistele. HELPDESK on MTÜ Lapikute poolt pakutav arvutiabiteenus. Teenus on suunatud tudengitele, õppejõududele ning kõikidele huvilistele.
</p> </p>
<div style="color: var(--orange)" class="mt-4 font-semibold flex items-center gap-2"> <div class="mt-4 font-semibold flex items-center gap-2 text-orange-500">
Vaata teenuseid <ArrowRight size={16} /> Vaata teenuseid <ArrowRight size={16} />
</div> </div>
</Card> </Card>
<Card variant="blur" href="/ourwork"> <Card variant="blur" href="/ourwork">
<div class="feature-icon" style="color: var(--orange)"> <div class="feature-icon text-orange-500">
<HeartHandshake size={32} strokeWidth={1.5} /> <HeartHandshake size={32} strokeWidth={1.5} />
</div> </div>
<h3 class="feature-title">Ettevõttele</h3> <h3 class="feature-title">Ettevõttele</h3>
<p class="feature-description"> <p class="feature-description">
Aitame sinu ideed ellu viia! Meie kogenud tudengid on valmis teie projekte realiseerima. Aitame sinu ideed ellu viia! Meie kogenud tudengid on valmis teie projekte realiseerima.
</p> </p>
<div style="color: var(--orange)" class="mt-4 font-semibold flex items-center gap-2"> <div class="mt-4 font-semibold flex items-center gap-2 text-orange-500">
Tehtud tööd <ArrowRight size={16} /> Tehtud tööd <ArrowRight size={16} />
</div> </div>
</Card> </Card>
@@ -82,7 +82,7 @@
<!-- What We Do Section --> <!-- What We Do Section -->
<Section> <Section>
<Grid gap="var(--space-4)"> <Grid gap="gap-4">
<Card variant="blur"> <Card variant="blur">
<h2 class="pb-4 text-4xl font-bold">Mida me loome</h2> <h2 class="pb-4 text-4xl font-bold">Mida me loome</h2>
<p>Meie põhieesmärgiks on Tallinna Tehnikaülikooli IT tudengitele erialase lisandväärtuse loomine. Kaasame tudengeid praktilistesse lahendustesse, kus saab ideid reaalselt ellu viia ja oma oskusi päriselt kasutada. Meie projektidest kasvavad välja huvitavad ja tarvilikud lõputööd, millel on selge praktiline väärtus.</p> <p>Meie põhieesmärgiks on Tallinna Tehnikaülikooli IT tudengitele erialase lisandväärtuse loomine. Kaasame tudengeid praktilistesse lahendustesse, kus saab ideid reaalselt ellu viia ja oma oskusi päriselt kasutada. Meie projektidest kasvavad välja huvitavad ja tarvilikud lõputööd, millel on selge praktiline väärtus.</p>
@@ -97,7 +97,7 @@
<!-- Partners Section --> <!-- Partners Section -->
<Section> <Section>
<div class="text-center mb-12"> <div class="text-center mb-12">
<h2 class="text-5xl"> <h2 class="text-5xl text-orange-500">
Koostööpartnerid Koostööpartnerid
</h2> </h2>
</div> </div>
@@ -107,7 +107,7 @@
{#if partner.url} {#if partner.url}
<a href={partner.url} target="_blank" rel="noopener noreferrer" class="partner-item" title={partner.name}> <a href={partner.url} target="_blank" rel="noopener noreferrer" class="partner-item" title={partner.name}>
{#if partner.image} {#if partner.image}
<img src={`/assets/partners_logos/${partner.image}`} alt={partner.name} /> <img src={`/assets/partners/${partner.image}`} alt={partner.name} />
{:else} {:else}
<span class="partner-name">{partner.name}</span> <span class="partner-name">{partner.name}</span>
{/if} {/if}
@@ -115,7 +115,7 @@
{:else} {:else}
<div class="partner-item"> <div class="partner-item">
{#if partner.image} {#if partner.image}
<img src={`/assets/partners_logos/${partner.image}`} alt={partner.name} /> <img src={`/assets/partners/${partner.image}`} alt={partner.name} />
{:else} {:else}
<span class="partner-name">{partner.name}</span> <span class="partner-name">{partner.name}</span>
{/if} {/if}
@@ -126,24 +126,7 @@
</Section> </Section>
<style> <style>
.feature-icon { /* PARTNERS SECTION */
margin-bottom: var(--space-3);
display: inline-flex;
}
.feature-title {
font-size: var(--fs-xl);
font-weight: 700;
margin-bottom: var(--space-2);
color: var(--black);
}
.feature-description {
color: var(--gray-800);
line-height: 1.6;
margin-bottom: var(--space-3);
}
.partner-item { .partner-item {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -0,0 +1,16 @@
<script context="module">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Svg from '../components/Svg.svelte';
const { Story } = defineMeta({
title: 'Components/Svg',
component: Svg,
});
</script>
<Story name="connector" args={{ type: 'connector' }} />
<Story name="mirrorV" args={{ type: 'mirrorV' }} />
<Story name="mirrorH" args={{ type: 'mirrorH' }} />
<Story name="mirrorVH" args={{ type: 'mirrorVH' }} />
<Story name="branch" args={{ type: 'branch' }} />
<Story name="branchH" args={{ type: 'branchH' }} />

View File

@@ -4,39 +4,5 @@ export default {
"./index.html", "./index.html",
"./src/**/*.{svelte,js,ts,jsx,tsx}", "./src/**/*.{svelte,js,ts,jsx,tsx}",
], ],
theme: {
extend: {
colors: {
orange: {
DEFAULT: '#f0941d',
light: '#ffa940',
dark: '#d67d0a',
500: '#f0941d',
},
gray: {
50: '#fafafa',
100: '#f5f5f5',
200: '#e5e5e5',
300: '#d4d4d4',
800: '#262626',
900: '#171717',
},
},
spacing: {
'0': '0px',
'1': '4px',
'2': '8px',
'3': '16px',
'4': '24px',
'5': '32px',
'6': '48px',
},
maxWidth: {
'2xl': '700px',
'3xl': '800px',
'4xl': '900px',
},
},
},
plugins: [], plugins: [],
} }