Pilotze, Twoj statek kosmiczny musi dzialac rownie dobrze na malym ekranie nadrecznego terminala, co na wielkim monitorze centrum dowodzenia. W swiecie React responsive design oznacza tworzenie komponentow, ktore elegancko dostosowuja sie do kazdego rozmiaru ekranu -- od mobilnego urządzenia po ultraszeroki monitor.
Czasem potrzebujesz zmienic nie tylko style, ale cala strukture komponentu w zaleznosci od rozmiaru ekranu. Wtedy przydaje sie custom hook
useMediaQuery:1function useMediaQuery(query) {
2 const [matches, setMatches] = useState(
3 () => window.matchMedia(query).matches
4 );
5
6 useEffect(() => {
7 const mediaQuery = window.matchMedia(query);
8 const handler = (e) => setMatches(e.matches);
9
10 mediaQuery.addEventListener('change', handler);
11 return () => mediaQuery.removeEventListener('change', handler);
12 }, [query]);
13
14 return matches;
15}Uzycie w komponencie:
1function MissionDashboard() {
2 const isMobile = useMediaQuery('(max-width: 767px)');
3 const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');
4
5 if (isMobile) return <MobileDashboard />;
6 if (isTablet) return <TabletDashboard />;
7 return <DesktopDashboard />;
8}Hook slucha na zmiany rozmiaru okna i zwraca
true/false -- React automatycznie przerenderuje komponent gdy wartosc sie zmieni.Podejscie mobile-first oznacza: zaczynamy od stylow dla najmalszych ekranow, a potem rozszerzamy dla wiekszych. Tak jak projektowanie kokpitu od najprostszego terminala w gore:
1/* Bazowe style -- mobile */
2.grid {
3 display: grid;
4 grid-template-columns: 1fr;
5 gap: 16px;
6}
7
8/* Tablet -- rozszerzamy */
9@media (min-width: 768px) {
10 .grid {
11 grid-template-columns: repeat(2, 1fr);
12 }
13}
14
15/* Desktop -- jeszcze szerzej */
16@media (min-width: 1024px) {
17 .grid {
18 grid-template-columns: repeat(3, 1fr);
19 }
20}Zamiast jednego ogromnego komponentu z wieloma warunkami, warto dzielic UI na mniejsze czesci, ktore mozna roznie komponowac:
1function Sidebar({ items }) {
2 return (
3 <nav className="sidebar">
4 {items.map(item => (
5 <a key={item.id} href={item.href}>{item.label}</a>
6 ))}
7 </nav>
8 );
9}
10
11function BottomNav({ items }) {
12 return (
13 <nav className="bottom-nav">
14 {items.map(item => (
15 <a key={item.id} href={item.href}>{item.icon}</a>
16 ))}
17 </nav>
18 );
19}
20
21function Navigation({ items }) {
22 const isMobile = useMediaQuery('(max-width: 767px)');
23 return isMobile ? <BottomNav items={items} /> : <Sidebar items={items} />;
24}Na mobilce nawigacja jest na dole ekranu (jak w aplikacji), a na desktopie -- z boku jako sidebar. Ten sam zestaw danych, rozna prezentacja.
Container queries to nowa, potezna funkcja CSS. Pozwalaja stylowac element na podstawie rozmiaru jego rodzica, a nie calego okna przegladarki:
1.card-container {
2 container-type: inline-size;
3}
4
5@container (min-width: 400px) {
6 .card {
7 display: flex;
8 gap: 16px;
9 }
10}
11
12@container (min-width: 600px) {
13 .card {
14 grid-template-columns: 1fr 2fr;
15 }
16}W React ustawiamy
container-type na rodzicu:1function CardGrid({ children }) {
2 return (
3 <div style={{ containerType: 'inline-size' }}>
4 {children}
5 </div>
6 );
7}Container queries sa swietne dla komponentow reuzywanych -- karta misji moze wygladac inaczej w waskim sidebarze i szerokim main content, bez wiedzy o rozmiarze okna.
Zamiast skokowo zmieniać rozmiar czcionki miedzy breakpointami, mozna uzyc
clamp() dla plynnego skalowania:1.heading {
2 /* Minimalnie 18px, optymalnie 4vw, maksymalnie 36px */
3 font-size: clamp(18px, 4vw, 36px);
4}
5
6.body-text {
7 font-size: clamp(14px, 2vw, 18px);
8 line-height: clamp(20px, 3vw, 28px);
9}W React mozna to zaimplementowac jako system:
1const fluidType = {
2 heading: 'clamp(18px, 4vw, 36px)',
3 subheading: 'clamp(16px, 3vw, 24px)',
4 body: 'clamp(14px, 2vw, 18px)',
5 caption: 'clamp(11px, 1.5vw, 14px)',
6};
7
8function Heading({ children }) {
9 return <h1 style={{ fontSize: fluidType.heading }}>{children}</h1>;
10}Obrazy to czesto najciezsze zasoby na stronie. React pozwala implementowac responsywne obrazy:
1function ResponsiveImage({ src, alt, sizes }) {
2 return (
3 <img
4 src={src}
5 alt={alt}
6 srcSet={`${src}?w=400 400w, ${src}?w=800 800w, ${src}?w=1200 1200w`}
7 sizes={sizes || '(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw'}
8 loading="lazy"
9 style={{ width: '100%', height: 'auto' }}
10 />
11 );
12}Atrybut
srcSet mowi przegladarce o dostepnych rozmiarach, a sizes -- ktory rozmiar uzyc przy danym rozmiarze okna.