zweisprachigkeit implementiert
This commit is contained in:
parent
c587bec2c7
commit
d99c0e2429
|
|
@ -1,7 +1,12 @@
|
||||||
|
import { LanguageProvider } from '../contexts/LanguageContext';
|
||||||
import AppRouter from './AppRouter';
|
import AppRouter from './AppRouter';
|
||||||
|
|
||||||
function PortfolioApp() {
|
function PortfolioApp() {
|
||||||
return <AppRouter />;
|
return (
|
||||||
|
<LanguageProvider>
|
||||||
|
<AppRouter />
|
||||||
|
</LanguageProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PortfolioApp;
|
export default PortfolioApp;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
import '../scss/language-toggle.scss';
|
||||||
|
|
||||||
|
export default function LanguageToggle() {
|
||||||
|
const { language, setLanguage } = useLanguage();
|
||||||
|
|
||||||
|
const handleLanguageChange = (newLang: 'en' | 'de') => {
|
||||||
|
if (newLang !== language) {
|
||||||
|
setLanguage(newLang);
|
||||||
|
// Announce language change to screen readers
|
||||||
|
const announcement = document.createElement('div');
|
||||||
|
announcement.setAttribute('role', 'status');
|
||||||
|
announcement.setAttribute('aria-live', 'polite');
|
||||||
|
announcement.setAttribute('class', 'sr-only');
|
||||||
|
announcement.textContent = `Language changed to ${newLang === 'en' ? 'English' : 'Deutsch'}`;
|
||||||
|
document.body.appendChild(announcement);
|
||||||
|
setTimeout(() => announcement.remove(), 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="language-toggle" role="group" aria-label="Language selection">
|
||||||
|
<button
|
||||||
|
className={`language-toggle__button ${language === 'en' ? 'language-toggle__button--active' : ''}`}
|
||||||
|
onClick={() => handleLanguageChange('en')}
|
||||||
|
aria-label="Switch to English"
|
||||||
|
aria-pressed={language === 'en'}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
EN
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`language-toggle__button ${language === 'de' ? 'language-toggle__button--active' : ''}`}
|
||||||
|
onClick={() => handleLanguageChange('de')}
|
||||||
|
aria-label="Zu Deutsch wechseln"
|
||||||
|
aria-pressed={language === 'de'}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
DE
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { getTexts } from '../config/texts';
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
|
||||||
type Props = Readonly<{
|
type Props = Readonly<{
|
||||||
theme: 'light' | 'dark';
|
theme: 'light' | 'dark';
|
||||||
|
|
@ -10,7 +10,7 @@ export default function ThemeToggle({
|
||||||
theme,
|
theme,
|
||||||
setTheme
|
setTheme
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const texts = getTexts();
|
const { texts } = useLanguage();
|
||||||
|
|
||||||
const toggleTheme = () => {
|
const toggleTheme = () => {
|
||||||
setTheme(theme === 'light' ? 'dark' : 'light');
|
setTheme(theme === 'light' ? 'dark' : 'light');
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { Github, Linkedin, Mail } from 'lucide-react';
|
import { Github, Linkedin, Mail } from 'lucide-react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { personalConfig, createEmailLink } from '../../config/personal';
|
import { personalConfig, createEmailLink } from '../../config/personal';
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
const texts = getTexts();
|
const { texts } = useLanguage();
|
||||||
// Email obfuscation function using config
|
// Email obfuscation function using config
|
||||||
const handleEmailClick = (e: React.MouseEvent) => {
|
const handleEmailClick = (e: React.MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||||
import ThemeToggle from '../ThemeToggle';
|
import ThemeToggle from '../ThemeToggle';
|
||||||
|
import LanguageToggle from '../LanguageToggle';
|
||||||
import MobileMenu from './MobileMenu';
|
import MobileMenu from './MobileMenu';
|
||||||
import { personalConfig } from '../../config/personal';
|
import { personalConfig } from '../../config/personal';
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import { scrollToSection } from '../../utils/scrollUtils';
|
import { scrollToSection } from '../../utils/scrollUtils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -18,7 +19,7 @@ export default function Navbar({
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const texts = getTexts();
|
const { texts } = useLanguage();
|
||||||
|
|
||||||
const menuItems = texts.navigation.menuItems;
|
const menuItems = texts.navigation.menuItems;
|
||||||
|
|
||||||
|
|
@ -43,6 +44,7 @@ export default function Navbar({
|
||||||
{item}
|
{item}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
<LanguageToggle />
|
||||||
<ThemeToggle theme={theme} setTheme={setTheme} />
|
<ThemeToggle theme={theme} setTheme={setTheme} />
|
||||||
{/* Burger Button für Mobile */}
|
{/* Burger Button für Mobile */}
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Code, Palette, ShieldCheck, Eye } from 'lucide-react';
|
import { Code, Palette, ShieldCheck, Eye } from 'lucide-react';
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import type { Service } from '../../data/Service';
|
import type { Service } from '../../data/Service';
|
||||||
import saschaImage from '../../assets/sascha.png';
|
import saschaImage from '../../assets/sascha.png';
|
||||||
|
|
||||||
|
|
@ -13,8 +13,9 @@ interface AboutSectionProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AboutSection(props: AboutSectionProps = {}) {
|
export default function AboutSection(props: AboutSectionProps = {}) {
|
||||||
const texts = getTexts().about;
|
const { texts: allTexts } = useLanguage();
|
||||||
const accessibilityTexts = getTexts().accessibility;
|
const texts = allTexts.about;
|
||||||
|
const accessibilityTexts = allTexts.accessibility;
|
||||||
|
|
||||||
// Use centralized texts as defaults, allow props to override
|
// Use centralized texts as defaults, allow props to override
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Award } from 'lucide-react';
|
import { Award } from 'lucide-react';
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import type { Certification } from '../../data/Certification';
|
import type { Certification } from '../../data/Certification';
|
||||||
|
|
||||||
interface CertificationsSectionProps {
|
interface CertificationsSectionProps {
|
||||||
|
|
@ -9,8 +9,9 @@ interface CertificationsSectionProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CertificationsSection(props: CertificationsSectionProps = {}) {
|
export default function CertificationsSection(props: CertificationsSectionProps = {}) {
|
||||||
const texts = getTexts().certifications;
|
const { texts: allTexts } = useLanguage();
|
||||||
const accessibilityTexts = getTexts().accessibility;
|
const texts = allTexts.certifications;
|
||||||
|
const accessibilityTexts = allTexts.accessibility;
|
||||||
|
|
||||||
// Use centralized texts as defaults, allow props to override
|
// Use centralized texts as defaults, allow props to override
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Mail, ExternalLink, Send } from 'lucide-react';
|
import { Mail, ExternalLink, Send } from 'lucide-react';
|
||||||
import { personalConfig, getObfuscatedEmail } from '../../config/personal';
|
import { personalConfig, getObfuscatedEmail } from '../../config/personal';
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
|
|
||||||
interface ContactSectionProps {
|
interface ContactSectionProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
|
@ -19,7 +19,8 @@ interface ContactSectionProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ContactSection(props: ContactSectionProps = {}) {
|
export default function ContactSection(props: ContactSectionProps = {}) {
|
||||||
const texts = getTexts().contact;
|
const { texts: allTexts } = useLanguage();
|
||||||
|
const texts = allTexts.contact;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
title = texts.title,
|
title = texts.title,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { Download } from 'lucide-react';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import type { StatItem } from '../../data/StatItem';
|
import type { StatItem } from '../../data/StatItem';
|
||||||
import { scrollToProjects } from '../../utils/scrollUtils';
|
import { scrollToProjects } from '../../utils/scrollUtils';
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import resumePDF from '../../assets/CV_Sascha_Bach.pdf';
|
import resumePDF from '../../assets/CV_Sascha_Bach.pdf';
|
||||||
|
|
||||||
interface HeroSectionProps {
|
interface HeroSectionProps {
|
||||||
|
|
@ -15,8 +15,9 @@ interface HeroSectionProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HeroSection(props: HeroSectionProps = {}) {
|
export default function HeroSection(props: HeroSectionProps = {}) {
|
||||||
const texts = getTexts().hero;
|
const { texts: allTexts } = useLanguage();
|
||||||
const accessibilityTexts = getTexts().accessibility;
|
const texts = allTexts.hero;
|
||||||
|
const accessibilityTexts = allTexts.accessibility;
|
||||||
|
|
||||||
// Use centralized texts as defaults, allow props to override
|
// Use centralized texts as defaults, allow props to override
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { GitBranch, ExternalLink } from 'lucide-react';
|
import { GitBranch, ExternalLink } from 'lucide-react';
|
||||||
import { useState, useRef, useEffect, type KeyboardEvent } from 'react';
|
import { useState, useRef, useEffect, type KeyboardEvent } from 'react';
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import { projectsData } from '../../config/projects-config';
|
import { projectsData } from '../../config/projects-config';
|
||||||
import type { Project } from '../../data/Project';
|
import type { Project } from '../../data/Project';
|
||||||
|
|
||||||
|
|
@ -12,8 +12,9 @@ interface ProjectsSectionProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
||||||
const texts = getTexts().projects;
|
const { texts: allTexts } = useLanguage();
|
||||||
const accessibilityTexts = getTexts().accessibility;
|
const texts = allTexts.projects;
|
||||||
|
const accessibilityTexts = allTexts.accessibility;
|
||||||
const [activeCardIndex, setActiveCardIndex] = useState<number | null>(null);
|
const [activeCardIndex, setActiveCardIndex] = useState<number | null>(null);
|
||||||
const [focusedActionIndex, setFocusedActionIndex] = useState<number>(0);
|
const [focusedActionIndex, setFocusedActionIndex] = useState<number>(0);
|
||||||
const cardRefs = useRef<(HTMLElement | null)[]>([]);
|
const cardRefs = useRef<(HTMLElement | null)[]>([]);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Eye, Lightbulb, Palette, Laptop, Rocket, ShieldCheck } from 'lucide-react';
|
import { Eye, Lightbulb, Palette, Laptop, Rocket, ShieldCheck } from 'lucide-react';
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import type { Service } from '../../data/Service';
|
import type { Service } from '../../data/Service';
|
||||||
|
|
||||||
interface ServicesSectionProps {
|
interface ServicesSectionProps {
|
||||||
|
|
@ -9,8 +9,9 @@ interface ServicesSectionProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ServicesSection(props: ServicesSectionProps = {}) {
|
export default function ServicesSection(props: ServicesSectionProps = {}) {
|
||||||
const texts = getTexts().services;
|
const { texts: allTexts } = useLanguage();
|
||||||
const accessibilityTexts = getTexts().accessibility;
|
const texts = allTexts.services;
|
||||||
|
const accessibilityTexts = allTexts.accessibility;
|
||||||
|
|
||||||
// Use centralized texts as defaults, allow props to override
|
// Use centralized texts as defaults, allow props to override
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { getTexts } from '../../config/texts';
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import type { Skill } from '../../data/Skill';
|
import type { Skill } from '../../data/Skill';
|
||||||
|
|
||||||
interface SkillCategory {
|
interface SkillCategory {
|
||||||
|
|
@ -27,8 +27,9 @@ const getColorByPosition = (index: number): string => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function SkillsSection(props: SkillsSectionProps = {}) {
|
export default function SkillsSection(props: SkillsSectionProps = {}) {
|
||||||
const texts = getTexts().skills;
|
const { texts: allTexts } = useLanguage();
|
||||||
const accessibilityTexts = getTexts().accessibility;
|
const texts = allTexts.skills;
|
||||||
|
const accessibilityTexts = allTexts.accessibility;
|
||||||
|
|
||||||
// Use centralized texts as defaults, allow props to override
|
// Use centralized texts as defaults, allow props to override
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
|
|
@ -209,23 +209,23 @@ export interface TextConfig {
|
||||||
privacyPolicy: {
|
privacyPolicy: {
|
||||||
title: string;
|
title: string;
|
||||||
lastUpdated: string;
|
lastUpdated: string;
|
||||||
|
introTitle: string;
|
||||||
|
introText: string;
|
||||||
dataCollectionTitle: string;
|
dataCollectionTitle: string;
|
||||||
dataCollectionText: string;
|
dataCollectionText: string;
|
||||||
dataCollectionList: string[];
|
dataCollectionList: string[];
|
||||||
purposeTitle: string;
|
webhostingTitle: string;
|
||||||
purposeText: string;
|
webhostingText: string;
|
||||||
providerTitle: string;
|
webhostingProvider: string;
|
||||||
providerText: string;
|
webhostingProviderName: string;
|
||||||
retentionTitle: string;
|
webhostingProviderAddress: string;
|
||||||
retentionText: string;
|
webhostingProviderWebsite: string;
|
||||||
rightsTitle: string;
|
rightsTitle: string;
|
||||||
rightsText: string;
|
rightsText: string;
|
||||||
rightsList: string[];
|
rightsList: string[];
|
||||||
contactTitle: string;
|
contactTitle: string;
|
||||||
contactText: string;
|
contactText: string;
|
||||||
contactEmail: string;
|
contactEmail: string;
|
||||||
legalBasisTitle: string;
|
|
||||||
legalBasisText: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Accessibility and Navigation
|
// Accessibility and Navigation
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import portfolioImage from '../assets/portfolio.PNG';
|
||||||
import ergoVRImage from '../assets/ErgoVR.PNG';
|
import ergoVRImage from '../assets/ErgoVR.PNG';
|
||||||
import icaraceImage from '../assets/icarace.PNG';
|
import icaraceImage from '../assets/icarace.PNG';
|
||||||
import dancaAlegriaImage from '../assets/Danca-Alegria.png';
|
import dancaAlegriaImage from '../assets/Danca-Alegria.png';
|
||||||
import { personalConfig } from './personal';
|
|
||||||
import { projects } from './locales/en/projects';
|
import { projects } from './locales/en/projects';
|
||||||
import type { Project } from '../data/Project';
|
import type { Project } from '../data/Project';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { createContext, useContext, useState, useEffect, type ReactNode } from 'react';
|
||||||
|
import { en, type TextConfig } from '../config/locales/en';
|
||||||
|
import { de } from '../config/locales/de';
|
||||||
|
|
||||||
|
type Language = 'en' | 'de';
|
||||||
|
|
||||||
|
interface LanguageContextType {
|
||||||
|
language: Language;
|
||||||
|
setLanguage: (lang: Language) => void;
|
||||||
|
texts: TextConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
const LANGUAGE_KEY = 'preferred-language';
|
||||||
|
|
||||||
|
function getBrowserLanguage(): Language {
|
||||||
|
const browserLang = navigator.language.toLowerCase();
|
||||||
|
if (browserLang.startsWith('de')) {
|
||||||
|
return 'de';
|
||||||
|
}
|
||||||
|
return 'en'; // Default to English for all other languages
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSavedLanguage(): Language | null {
|
||||||
|
const saved = localStorage.getItem(LANGUAGE_KEY);
|
||||||
|
if (saved === 'en' || saved === 'de') {
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LanguageProvider({ children }: { children: ReactNode }) {
|
||||||
|
const [language, setLanguageState] = useState<Language>(() => {
|
||||||
|
// First check localStorage, then browser language, then default to English
|
||||||
|
return getSavedLanguage() || getBrowserLanguage();
|
||||||
|
});
|
||||||
|
|
||||||
|
const texts = language === 'de' ? de : en;
|
||||||
|
|
||||||
|
const setLanguage = (lang: Language) => {
|
||||||
|
setLanguageState(lang);
|
||||||
|
localStorage.setItem(LANGUAGE_KEY, lang);
|
||||||
|
// Update HTML lang attribute for accessibility and SEO
|
||||||
|
document.documentElement.lang = lang;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Set initial lang attribute
|
||||||
|
document.documentElement.lang = language;
|
||||||
|
}, [language]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LanguageContext.Provider value={{ language, setLanguage, texts }}>
|
||||||
|
{children}
|
||||||
|
</LanguageContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLanguage() {
|
||||||
|
const context = useContext(LanguageContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useLanguage must be used within a LanguageProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,8 @@ import { personalConfig } from '../config/personal';
|
||||||
import { getTexts } from '../config/texts';
|
import { getTexts } from '../config/texts';
|
||||||
|
|
||||||
export default function ImprintPage() {
|
export default function ImprintPage() {
|
||||||
const texts = getTexts().imprint;
|
const { texts: allTexts } = useLanguage();
|
||||||
|
const texts = allTexts.imprint;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Prevent indexing of this page
|
// Prevent indexing of this page
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { getTexts } from '../config/texts';
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
|
||||||
export default function PrivacyPolicy() {
|
export default function PrivacyPolicy() {
|
||||||
const texts = getTexts().privacyPolicy;
|
const { texts: allTexts } = useLanguage();
|
||||||
|
const texts = allTexts.privacyPolicy;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Prevent indexing of this page
|
// Prevent indexing of this page
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
@use './variables' as *;
|
||||||
|
@use './globals';
|
||||||
|
|
||||||
|
.language-toggle {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--color-text-muted);
|
||||||
|
border-radius: $border-radius-sm;
|
||||||
|
padding: 0.25rem;
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
@extend %hover-lift;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid var(--color-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
box-shadow: 0 2px 4px var(--box-shadow-active);
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-background);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
&__button {
|
&__button {
|
||||||
@extend %button-reset;
|
@extend %button-reset;
|
||||||
@extend %hover-lift;
|
@extend %hover-lift;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue