Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,21 @@
.page > .page-spacer {
flex: 0 0 300px;
}

@media (max-width: 700px) {
.page {
flex-direction: column; /* sidebar havner over main i stedet for ved siden av */
padding-top: 16px;
padding-left: 16px;
padding-right: 16px;
gap: 16px;
}

.page > main {
padding-bottom: 72px; /* plass til bunnlinja - uten den havner siste filmkort bak den */
}

.page > .page-spacer {
display: none; /* en tom kolonne er bortkastet plass på mobil */
}
}
139 changes: 112 additions & 27 deletions src/components/Sidebar.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,26 @@ html {
.sidebar {
text-align: left;
flex: 0 0 300px;
position: sticky; /*betyr at elementet oppfører seg helt normalt helt til man scroller forbi "top" grensen som man også setter */
position: sticky; /* oppfører seg normalt til man scroller forbi "top"-grensen */
top: 24px;
/*
Tips fra claude her:
når dere gjør siden responsiv er overflow-x: hidden på body eller #root
et vanlig triks mot horisontal scroll, og det dreper sticky umiddelbart uten noen feilmelding.
Hvis sidebaren blir høyere enn skjermen (flere lenker, eller favoritter listet i sidebaren senere),
legg til max-height: 100vh; og overflow-y: auto; i samme regel. Da får sidebaren sin egen scroll i
stedet for at bunnen blir utilgjengelig.
*/
}

/* ---------- Desktop: sidebar med undermeny ---------- */

.sidebar-nav {
list-style: none; /* fjerner kulepunktene, som i skissen */
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 18px; /* avstand mellom lenkene */
gap: 18px;
}



.sidebar-nav a {
display: block; /* gjør hele raden klikkbar, ikke bare bokstavene */
font-size: 17px; /* var 28px - hovedgrunnen til at sidebaren dominerte */
font-size: 17px;
padding: 10px 14px;
border-radius: 10px; /* samme runding som filmkortene */
border-radius: 10px;
text-decoration: none;
color: var(--text-h);
}
Expand All @@ -48,17 +40,20 @@ html {
outline-offset: 2px;
}



/* HOME og pila ligger side om side i én boks */
.sidebar-nav__row {
display: flex;
align-items: stretch; /* så delelinja går i full høyde */
}

.sidebar-nav__home {
display: flex; /* var block - flex lar pila ligge helt til høyre */
display: flex;
align-items: center;
justify-content: space-between;
justify-content: flex-start;
width: 100%;
font: inherit;
font-size: 17px;
font-weight: 600; /* HOME er overordnet, så litt tyngre enn underpunktene */
font-weight: 600;
padding: 10px 14px;
border: none;
background: none;
Expand All @@ -67,6 +62,12 @@ html {
cursor: pointer;
}

/* Gjelder kun HOME inne i raden. FAVORITTER står alene og beholder full runding. */
.sidebar-nav__row .sidebar-nav__home {
flex: 1;
border-radius: 10px 0 0 10px;
}

.sidebar-nav__home:hover {
background: var(--accent-bg);
color: var(--accent);
Expand All @@ -77,33 +78,117 @@ html {
outline-offset: 2px;
}

/* Pila som viser om menyen er åpen */
.sidebar-nav__home[aria-expanded]::after {
.sidebar-nav__home[aria-current="page"] {
background: var(--accent-bg);
color: var(--accent);
}

/* Pil-knappen. 44px er minstemålet for en touch-flate i WCAG. */
.sidebar-nav__toggle {
flex: none;
width: 44px;
border: none;
border-left: 1px solid var(--border); /* den diskré delelinja mot HOME */
border-radius: 0 10px 10px 0;
background: none;
color: var(--text-h);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}

.sidebar-nav__toggle::after {
content: "▾";
display: inline-block; /* transform virker ikke på inline-elementer */
display: inline-block; /* transform virker ikke på rene inline-elementer */
font-size: 13px;
transition: transform 0.2s ease;
}

.sidebar-nav__home[aria-expanded="false"]::after {
transform: rotate(-90deg); /* peker til siden når menyen er lukket */
.sidebar-nav__toggle[aria-expanded="false"]::after {
transform: rotate(-90deg);
}

.sidebar-nav__toggle:hover {
background: var(--accent-bg);
color: var(--accent);
}

.sidebar-nav__toggle:focus-visible {
outline: 2px solid var(--accent);
outline-offset: -2px; /* innover, så den ikke klippes av naboen */
}

.sidebar-nav__sub {
list-style: none;
margin: 4px 0 0 20px;
padding-left: 10px;
border-left: 2px solid var(--border); /* loddrett strek som binder punktene til HOME */
border-left: 2px solid var(--border); /* strek som binder punktene til HOME */
display: flex;
flex-direction: column;
gap: 2px;
}

.sidebar-nav__sub a {
font-size: 15px;
color: var(--text); /* svakere enn HOME, så hierarkiet synes */
color: var(--text);
}

.sidebar-nav__sub a:hover {
color: var(--accent);
}

/* ---------- Mobil: fast bunnlinje ---------- */
/* Trenger ingen media query: denne menyen rendres kun når skjermen er smal. */

.bottom-nav {
position: fixed; /* låst til skjermen, ikke til siden - blir stående ved scroll */
bottom: 0;
left: 0;
right: 0;
z-index: 20;
background: var(--bg);
border-top: 1px solid var(--border);
padding-bottom: env(safe-area-inset-bottom); /* plass til home-streken på iPhone */
}

.bottom-nav__list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
}

.bottom-nav__list li {
flex: 1; /* fire like brede knapper */
}

.bottom-nav__button {
width: 100%;
min-height: 48px; /* godt over 44px-minstemålet for touch */
font: inherit;
font-size: 13px;
padding: 10px 4px;
border: none;
background: none;
color: var(--text);
cursor: pointer;
}

.bottom-nav__button[aria-current="page"] {
color: var(--accent);
font-weight: 600;
}

.bottom-nav__button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: -2px;
}

@media (max-width: 700px) {
.sidebar {
flex: none; /* nullstiller de 300px */
width: 100%;
position: static; /* sticky gir ingen mening når den bare inneholder søkefeltet */
}
}
131 changes: 88 additions & 43 deletions src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { SearchField } from './SearchField';
import { useMediaQuery } from '../hooks/useMediaQuery';
import './Sidebar.css';

type SidebarProps = {
Expand All @@ -9,14 +10,24 @@ type SidebarProps = {
onViewChange: (view: 'hjem' | 'favoritter') => void;
};

// Underpunktene som vises når HOME er åpen
// Underpunktene som vises under HOME på desktop
const sections = [
{ id: "populaere", label: "Populære filmer" },
{ id: "nye-filmer", label: "Kommende filmer" },
];

// Bunnlinja på mobil. Kortere etiketter, fordi fire knapper deler skjermbredden -
// på 390px blir det under 100px hver.
const mobileItems = [
{ id: "hjem", label: "Hjem" },
{ id: "populaere", label: "Populære" },
{ id: "nye-filmer", label: "Kommende" },
{ id: "favoritter", label: "Favoritter" },
];

export function Sidebar({ onSearch, searchValue, view, onViewChange }: SidebarProps) {
const [isOpen, setIsOpen] = useState(true);
const isMobile = useMediaQuery('(max-width: 700px)');

// Tømmer søket, bytter tilbake til hjem-visningen og scroller.
// Timeouten venter til React har rendret seksjonene tilbake - de finnes
Expand All @@ -33,57 +44,91 @@ export function Sidebar({ onSearch, searchValue, view, onViewChange }: SidebarPr
}, 0);
}

function goToFavorites() {
onSearch?.('');
onViewChange('favoritter');
}

return (
<aside className="sidebar">

<SearchField value={searchValue ?? ''} onChange={(v) => onSearch?.(v)} />

<nav aria-label="Innhold på siden">
<ul className="sidebar-nav">
{isMobile ? (
// Mobil: fast bunnlinje med fire flate knapper, innen tommelens rekkevidde
<nav className="bottom-nav" aria-label="Hovedmeny">
<ul className="bottom-nav__list">
{mobileItems.map((item) => (
<li key={item.id}>
<button
type="button"
className="bottom-nav__button"
aria-current={
item.id === 'favoritter' && view === 'favoritter' ? 'page' : undefined
}
onClick={() => (item.id === 'favoritter' ? goToFavorites() : goToSection(item.id))}
>
{item.label}
</button>
</li>
))}
</ul>
</nav>
) : (
// Desktop: HOME med utslåbar undermeny
<nav aria-label="Hovedmeny">
<ul className="sidebar-nav">

<li>
{/* HOME og pila er to separate knapper: HOME går til toppen,
pila åpner og lukker undermenyen. */}
<div className="sidebar-nav__row">
<button
type="button"
className="sidebar-nav__home"
onClick={() => goToSection('hjem')}
>
HOME
</button>

<li>
{/* button og ikke a, fordi den utfører en handling (åpne/lukke).
aria-expanded gjør at skjermlesere sier "utvidet"/"sammenslått"
i stedet for bare "HOME". */}
<button
type="button"
className="sidebar-nav__home"
aria-expanded={isOpen}
onClick={() => {
setIsOpen(!isOpen);
goToSection('hjem');
}}
>
HOME
</button>
{/* aria-label er påkrevd her fordi knappen ikke har tekst i seg -
pila er tegnet i CSS, og CSS-innhold leses ikke opp. */}
<button
type="button"
className="sidebar-nav__toggle"
aria-expanded={isOpen}
aria-label={isOpen ? 'Skjul undermeny' : 'Vis undermeny'}
onClick={() => setIsOpen(!isOpen)}
/>
</div>

{/* Underlista rendres kun når isOpen er true */}
{isOpen && (
<ul className="sidebar-nav__sub">
{sections.map((section) => (
<li key={section.id}>
<a href={`#${section.id}`} onClick={(e) => { e.preventDefault(); goToSection(section.id); }}>
{section.label}
</a>
</li>
))}
</ul>
)}
</li>
{isOpen && (
<ul className="sidebar-nav__sub">
{sections.map((section) => (
<li key={section.id}>
<a href={`#${section.id}`} onClick={(e) => { e.preventDefault(); goToSection(section.id); }}>
{section.label}
</a>
</li>
))}
</ul>
)}
</li>

<li>
<button
type="button"
className="sidebar-nav__home"
aria-current={view === 'favoritter' ? 'page' : undefined}
onClick={() => { onSearch?.(''); onViewChange('favoritter'); }}
>
FAVORITTER
</button>
</li>
<li>
<button
type="button"
className="sidebar-nav__home"
aria-current={view === 'favoritter' ? 'page' : undefined}
onClick={goToFavorites}
>
FAVORITTER
</button>
</li>

</ul>
</nav>
</ul>
</nav>
)}
</aside>
);
}
Loading