mirror of
https://github.com/Lapikud/lapikud.github.io.git
synced 2026-08-09 02:19:13 +00:00
footer, navbar and components rework
This commit is contained in:
@@ -1,39 +1,22 @@
|
||||
<script>
|
||||
export let onClick = () => {};
|
||||
export let border = false;
|
||||
export let textAlign = "left";
|
||||
export let whiteSpace = "nowrap";
|
||||
export let borderRadius = "8px";
|
||||
export let buttonHoverStyle = "";
|
||||
|
||||
let isHovered = false;
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={onClick}
|
||||
class="btn"
|
||||
class:borderless={!border}
|
||||
style={`text-align: ${textAlign}; white-space: ${whiteSpace}; border-radius: ${borderRadius};`}
|
||||
{...$$restProps}
|
||||
<button
|
||||
on:click={onClick}
|
||||
on:mouseenter={() => (isHovered = true)}
|
||||
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 : null}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
background: var(--white);
|
||||
color: var(--black);
|
||||
border: 1.5px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.btn:not(.borderless) {
|
||||
border-color: var(--orange);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: var(--orange);
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
import Button from "./Button.svelte";
|
||||
|
||||
export let name = "";
|
||||
export let border = false;
|
||||
export let borderRadius = "8px";
|
||||
export let buttonClass = "";
|
||||
export let panelClassName = "";
|
||||
export let buttonHoverStyle = "";
|
||||
|
||||
let isOpen = false;
|
||||
|
||||
@@ -21,7 +22,7 @@
|
||||
</script>
|
||||
|
||||
<div class="relative inline-block" on:focusout={handleFocusOut}>
|
||||
<Button borderRadius={borderRadius} onClick={toggle} border={border}>
|
||||
<Button onClick={toggle} class={buttonClass} {buttonHoverStyle}>
|
||||
{name}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -41,10 +42,11 @@
|
||||
</Button>
|
||||
|
||||
<div
|
||||
class="absolute mt-2 rounded-md shadow-lg bg-white ring-1 ring-black/5 overflow-hidden flex flex-col"
|
||||
style="border-radius: {borderRadius}"
|
||||
class="{panelClassName} absolute mt-2 rounded-md shadow-lg overflow-visible flex flex-col"
|
||||
style:visibility={isOpen ? "visible" : "hidden"}
|
||||
>
|
||||
<slot />
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
<script>
|
||||
export let title = "";
|
||||
export let description = "";
|
||||
export let icon = null;
|
||||
export let href = "";
|
||||
export let variant = "default"; // 'default' | 'elevated' | 'bordered' | 'glass'
|
||||
export let iconColor = "var(--orange)";
|
||||
export let className = "";
|
||||
|
||||
const variantClasses = {
|
||||
default: "card-default",
|
||||
elevated: "card-elevated",
|
||||
bordered: "card-bordered",
|
||||
glass: "card-glass",
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a {href} class={`feature-card ${variantClasses[variant]} ${className}`}>
|
||||
{#if icon}
|
||||
<div class="feature-icon" style="color: {iconColor}">
|
||||
<svelte:component this={icon} size={32} strokeWidth={1.5} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if title}
|
||||
<h3 class="feature-title">{title}</h3>
|
||||
{/if}
|
||||
{#if description}
|
||||
<p class="feature-description">{description}</p>
|
||||
{/if}
|
||||
<div class="feature-content">
|
||||
<slot />
|
||||
</div>
|
||||
</a>
|
||||
{:else}
|
||||
<div class={`feature-card ${variantClasses[variant]} ${className}`}>
|
||||
{#if icon}
|
||||
<div class="feature-icon" style="color: {iconColor}">
|
||||
<svelte:component this={icon} size={32} strokeWidth={1.5} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if title}
|
||||
<h3 class="feature-title">{title}</h3>
|
||||
{/if}
|
||||
{#if description}
|
||||
<p class="feature-description">{description}</p>
|
||||
{/if}
|
||||
<div class="feature-content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.feature-card {
|
||||
position: relative;
|
||||
padding: var(--space-5);
|
||||
border-radius: var(--radius-lg);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
a.feature-card:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-default {
|
||||
background: var(--white);
|
||||
border: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.card-default:hover {
|
||||
box-shadow: var(--shadow-2);
|
||||
border-color: var(--orange);
|
||||
}
|
||||
|
||||
.card-elevated {
|
||||
background: var(--white);
|
||||
box-shadow: var(--shadow-2);
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.card-elevated:hover {
|
||||
box-shadow: var(--shadow-3);
|
||||
border-color: var(--orange);
|
||||
}
|
||||
|
||||
.card-bordered {
|
||||
background: transparent;
|
||||
border: 2px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.card-bordered:hover {
|
||||
border-color: var(--orange);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.card-glass {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.card-glass:hover {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: var(--shadow-glow);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
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);
|
||||
}
|
||||
|
||||
.feature-content {
|
||||
margin-top: var(--space-3);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.feature-card {
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-size: var(--fs-lg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,8 +4,6 @@ export { default as Stack } from './layout/Stack.svelte'
|
||||
export { default as Grid } from './layout/Grid.svelte'
|
||||
export { default as Center } from './layout/Center.svelte'
|
||||
export { default as Section } from './layout/Section.svelte'
|
||||
export { default as FullWidthBanner } from './layout/FullWidthBanner.svelte'
|
||||
|
||||
export { default as Button } from './Button.svelte'
|
||||
export { default as Dropdown } from './Dropdown.svelte'
|
||||
export { default as FeatureCard } from './FeatureCard.svelte'
|
||||
|
||||
@@ -1,12 +1,88 @@
|
||||
<!--
|
||||
A simple presentational container that renders a `div` with the `card` class.
|
||||
-->
|
||||
|
||||
<script>
|
||||
export let href = "";
|
||||
export let variant = "default"; // 'default' | 'animated' | 'animated-neutral' | 'glass'
|
||||
export let className = "";
|
||||
export let background = "var(--off-white)";
|
||||
|
||||
const variantClasses = {
|
||||
default: "card-default",
|
||||
animated: "card-animated",
|
||||
"animated-neutral": "card-animated-neutral",
|
||||
glass: "card-glass",
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class={`card ${className}`} style="background: {background}">
|
||||
<slot />
|
||||
</div>
|
||||
{#if href}
|
||||
<a {href} class={`card ${variantClasses[variant]} ${className}`}>
|
||||
<slot />
|
||||
</a>
|
||||
{:else}
|
||||
<div class={`card ${variantClasses[variant]} ${className}`}>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.card {
|
||||
position: relative;
|
||||
padding: var(--space-5);
|
||||
border-radius: var(--radius-lg);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
a.card:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-default {
|
||||
background: var(--white);
|
||||
border: 2px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.card-default:hover {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.card-animated {
|
||||
background: transparent;
|
||||
border: 2px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.card-animated:hover {
|
||||
border-color: var(--orange);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.card-animated-neutral {
|
||||
background: transparent;
|
||||
border: 2px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.card-animated-neutral:hover {
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.card-glass {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(20px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.card-glass:hover {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.card {
|
||||
padding: var(--space-4);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,9 +7,10 @@ projected via the default slot.
|
||||
|
||||
<script>
|
||||
export let maxWidth = false; // true removes max-width
|
||||
export let center = true; // false prevents centering and removes horizontal padding
|
||||
export let className = "";
|
||||
</script>
|
||||
|
||||
<div class={`container ${maxWidth ? "container--fluid" : ""} ${className}`}>
|
||||
<div class="w-full {center ? 'px-4 md:px-6 lg:px-8' : ''} {maxWidth ? '' : 'max-w-[1200px]'} {center ? 'mx-auto' : ''} {className}">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<!--
|
||||
Full-width colored section with configurable background and padding.
|
||||
Content is projected via the default slot.
|
||||
-->
|
||||
|
||||
<script>
|
||||
export let background = "var(--orange)";
|
||||
export let paddingX = "var(--site-padding)";
|
||||
export let paddingY = "var(--space-4)";
|
||||
export let className = "";
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="w-screen relative left-1/2 right-1/2 -ml-[50vw] -mr-[50vw] flex justify-center {className}"
|
||||
style="background: {background}; padding: {paddingY} {paddingX};"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
@@ -11,8 +11,8 @@ spacing. Content is projected into the default slot.
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={`grid ${className}`}
|
||||
style={`grid-template-columns: repeat(auto-fit, minmax(${min}, 1fr)); gap: ${gap};`}
|
||||
class="grid {className}"
|
||||
style="grid-template-columns: repeat(auto-fit, minmax({min}, 1fr)); gap: {gap};"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
const paddingClasses = {
|
||||
none: "py-0",
|
||||
small: "py-section-sm",
|
||||
normal: "py-section",
|
||||
large: "py-section-lg",
|
||||
small: "py-8",
|
||||
normal: "py-12 max-md:py-8",
|
||||
large: "py-[clamp(3rem,8vw,6rem)] max-md:py-12",
|
||||
};
|
||||
|
||||
// Convert background to appropriate CSS value
|
||||
@@ -26,59 +26,10 @@
|
||||
|
||||
<section
|
||||
{id}
|
||||
class={`section ${paddingClasses[padding]} ${className}`}
|
||||
class="w-full relative {paddingClasses[padding]} {className}"
|
||||
style={backgroundStyle ? `background: ${backgroundStyle};` : ""}
|
||||
>
|
||||
<div class="section-inner" class:full-width={fullWidth}>
|
||||
<div class="{fullWidth ? '' : 'max-w-[1200px]'} mx-auto px-(--site-padding)">
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.section {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section-inner {
|
||||
max-width: var(--content-max);
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--site-padding);
|
||||
}
|
||||
|
||||
.section-inner.full-width {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.py-0 {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.py-section-sm {
|
||||
padding-top: var(--space-5);
|
||||
padding-bottom: var(--space-5);
|
||||
}
|
||||
|
||||
.py-section {
|
||||
padding-top: var(--space-6);
|
||||
padding-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
.py-section-lg {
|
||||
padding-top: clamp(3rem, 8vw, 6rem);
|
||||
padding-bottom: clamp(3rem, 8vw, 6rem);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.py-section {
|
||||
padding-top: var(--space-5);
|
||||
padding-bottom: var(--space-5);
|
||||
}
|
||||
|
||||
.py-section-lg {
|
||||
padding-top: var(--space-6);
|
||||
padding-bottom: var(--space-6);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,6 +8,6 @@ A simple stack/column layout that stacks children vertically.
|
||||
export let className = "";
|
||||
</script>
|
||||
|
||||
<div class={`stack ${className}`} style={`gap: ${gap}; align-items: ${align};`}>
|
||||
<div class="flex flex-col {className}" style="gap: {gap}; align-items: {align};">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user