diff --git a/src/App.css b/src/App.css index fb43242..41a52fe 100644 --- a/src/App.css +++ b/src/App.css @@ -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 */ + } +} diff --git a/src/components/Sidebar.css b/src/components/Sidebar.css index 793b716..b8e8909 100644 --- a/src/components/Sidebar.css +++ b/src/components/Sidebar.css @@ -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); } @@ -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; @@ -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); @@ -77,23 +78,52 @@ 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; @@ -101,9 +131,64 @@ html { .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 */ + } } \ No newline at end of file diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 8dbaae3..049be58 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import { SearchField } from './SearchField'; +import { useMediaQuery } from '../hooks/useMediaQuery'; import './Sidebar.css'; type SidebarProps = { @@ -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 @@ -33,57 +44,91 @@ export function Sidebar({ onSearch, searchValue, view, onViewChange }: SidebarPr }, 0); } + function goToFavorites() { + onSearch?.(''); + onViewChange('favoritter'); + } + return ( ); } \ No newline at end of file diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts new file mode 100644 index 0000000..317f1cb --- /dev/null +++ b/src/hooks/useMediaQuery.ts @@ -0,0 +1,28 @@ +import { useState, useEffect } from 'react'; + +// Samme form som useSessionStorage: useState holder verdien, useEffect holder den +// i takt med noe utenfor React, og funksjonen returnerer verdien. +// +// Vi trenger denne i JSX og ikke bare i CSS fordi mobil og desktop har forskjellig +// DOM-struktur: på desktop ligger undermenyen nøstet inni HOME, på mobil skal alle +// fire ligge flatt ved siden av hverandre. CSS kan skjule og vise, men kan ikke +// flytte et element ut av forelderen sin. +export function useMediaQuery(query: string) { + // window.matchMedia er JavaScript-versjonen av en CSS media query. + // .matches er true eller false akkurat nå. + const [matches, setMatches] = useState(() => window.matchMedia(query).matches); + + useEffect(() => { + const mql = window.matchMedia(query); + const onChange = () => setMatches(mql.matches); + + // "change" fyrer i det vi krysser grensen, f.eks. når vinduet endrer størrelse + mql.addEventListener('change', onChange); + + // Opprydding: React kjører denne når komponenten forsvinner, ellers + // hoper lytterne seg opp for hver gang komponenten rendres på nytt. + return () => mql.removeEventListener('change', onChange); + }, [query]); + + return matches; +} \ No newline at end of file