From 10691bbcd32e51e4d88b249cc005a87155f4a4a4 Mon Sep 17 00:00:00 2001 From: Wilhelm Ringvold Date: Tue, 6 Oct 2026 11:07:27 +0200 Subject: [PATCH 1/2] basic html og css satt opp uten api eller mer --- t20-project-2/index.html | 173 ++++++++++++++++++- t20-project-2/src/index.css | 329 +++++++++++++++++++++++++----------- 2 files changed, 404 insertions(+), 98 deletions(-) diff --git a/t20-project-2/index.html b/t20-project-2/index.html index 3fb66a4..0a84d47 100644 --- a/t20-project-2/index.html +++ b/t20-project-2/index.html @@ -2,12 +2,177 @@ - - t20-project-2 + + + + Country Boys – World City Catalog -
- + + +
+
+
+

THE WORLD IS CLOSER THAN YOU THINK

+

Find the city that
speaks to you.

+

From quiet coastal towns to lively capitals. Your next discovery starts here.

+
+ +
+ + + +
+ + +
+
+
+

YOUR NEXT DESTINATION

+

Cities to explore 24,080

+
+ +
+ +
+ Europe + Clear all +
+ +
    +
  1. + Colorful houses along Nyhavn in Copenhagen +
    +

    DENMARK · EUROPE

    Copenhagen

    +

    Bike-friendly streets, Nordic design, and long summer evenings by the water.

    +
    4.8 (248)653,000 residents14 m above sea level
    + View city details +
    +
  2. + +
  3. + Yellow tram on a street in Lisbon +
    +

    PORTUGAL · EUROPE

    Lisbon

    +

    Pastel facades, steep hills, and the salty Atlantic breeze.

    +
    4.9 (391)545,000 residents2 m above sea level
    + View city details +
    +
  4. + +
  5. + Traditional Kyoto street with a pagoda in the background +
    +

    JAPAN · ASIA

    Kyoto

    +

    Timber houses, peaceful temple gardens, and a city in constant motion.

    +
    4.7 (184)1.46 million residents50 m above sea level
    + View city details +
    +
  6. +
+ + +
+
+ +
+

SEE THE WORLD FROM A NEW PERSPECTIVE

Where will curiosity take you?

Every pin is a city with a story of its own. Find it on the map.

Open the world map
+ +
+ + +
diff --git a/t20-project-2/src/index.css b/t20-project-2/src/index.css index 5fb3313..a7c9215 100644 --- a/t20-project-2/src/index.css +++ b/t20-project-2/src/index.css @@ -1,111 +1,252 @@ -:root { - --text: #6b6375; - --text-h: #08060d; - --bg: #fff; - --border: #e5e4e7; - --code-bg: #f4f3ec; - --accent: #aa3bff; - --accent-bg: rgba(170, 59, 255, 0.1); - --accent-border: rgba(170, 59, 255, 0.5); - --social-bg: rgba(244, 243, 236, 0.5); - --shadow: - rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; - - --sans: system-ui, 'Segoe UI', Roboto, sans-serif; - --heading: system-ui, 'Segoe UI', Roboto, sans-serif; - --mono: ui-monospace, Consolas, monospace; +@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Manrope:wght@400;500;600;700;800&display=swap'); - font: 18px/145% var(--sans); - letter-spacing: 0.18px; - color-scheme: light dark; - color: var(--text); - background: var(--bg); +:root { + font-family: 'DM Sans', sans-serif; + color: #1c2c28; + background: #f5f7f4; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - - @media (max-width: 1024px) { - font-size: 16px; - } + --ink: #1c2c28; + --muted: #697671; + --soft-muted: #89938e; + --green: #21745c; + --green-dark: #185844; + --green-pale: #e9f1ec; + --line: #e4e9e5; + --paper: #fff; + --sun: #f0aa5d; + --serif: 'Manrope', sans-serif; } -@media (prefers-color-scheme: dark) { - :root { - --text: #9ca3af; - --text-h: #f3f4f6; - --bg: #16171d; - --border: #2e303a; - --code-bg: #1f2028; - --accent: #c084fc; - --accent-bg: rgba(192, 132, 252, 0.15); - --accent-border: rgba(192, 132, 252, 0.5); - --social-bg: rgba(47, 48, 58, 0.5); - --shadow: - rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; - } +* { box-sizing: border-box; } +html { min-width: 320px; scroll-behavior: smooth; } +body { margin: 0; min-width: 320px; } +button, input, select { font: inherit; } +a { color: inherit; } +button, a, input, select { -webkit-tap-highlight-color: transparent; } +:focus-visible { outline: 3px solid #e29444; outline-offset: 3px; } +.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } - #social .button-icon { - filter: invert(1) brightness(2); - } -} - -#root { - width: 1126px; - max-width: 100%; - margin: 0 auto; - text-align: center; - border-inline: 1px solid var(--border); - min-height: 100svh; +.site-header { + height: 76px; + padding: 0 max(32px, calc((100vw - 1260px) / 2)); display: flex; - flex-direction: column; - box-sizing: border-box; + align-items: center; + justify-content: space-between; + background: rgba(255,255,255,.94); + border-bottom: 1px solid #e8ece8; } +.brand, .footer-brand { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; color: var(--ink); font: 800 23px/1 var(--serif); letter-spacing: 0; } +.brand-mark { display: grid; width: 34px; height: 34px; place-items: center; color: var(--green); } +.brand-mark svg { width: 33px; height: 33px; } +.brand-period, .footer-brand span { color: #e29a4d; } +.main-nav { height: 100%; display: flex; align-items: center; gap: 38px; margin-left: -28px; } +.nav-link { position: relative; height: 100%; display: inline-flex; align-items: center; color: #66716c; font-size: 14px; text-decoration: none; } +.nav-link:hover, .nav-link-active { color: var(--green-dark); } +.nav-link-active { font-weight: 700; } +.nav-link-active::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background: var(--green); } +.login-link { display: inline-flex; align-items: center; gap: 8px; padding: 10px 15px; border: 1px solid #dce4df; border-radius: 6px; background: #fff; color: var(--ink); text-decoration: none; font-size: 13px; font-weight: 600; transition: border-color .2s, background-color .2s; } +.login-link:hover { border-color: #a7bdb1; background: #f8faf8; } +.login-icon, .login-icon svg { display: block; width: 18px; height: 18px; } -body { - margin: 0; -} +.page-shell { width: min(1184px, calc(100% - 64px)); margin: 0 auto; } +.intro { position: relative; min-height: 230px; margin-top: 34px; display: flex; align-items: center; justify-content: space-between; overflow: hidden; } +.intro-copy { position: relative; z-index: 1; padding: 15px 0 19px; } +.eyebrow, .results-kicker { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; color: var(--green); font-size: 10px; font-weight: 700; letter-spacing: 1.45px; } +.eyebrow-dot, .caption-dot { width: 7px; height: 7px; border-radius: 50%; background: #e5a151; } +h1, h2, h3, p { margin-top: 0; } +h1 { margin-bottom: 10px; color: var(--ink); font: 700 42px/1.15 var(--serif); letter-spacing: 0; } +h1 span { color: var(--green); } +.intro-description { margin: 0; color: #697671; font-size: 14px; } +.intro-image { position: absolute; top: 0; right: 0; width: 45%; height: 100%; border-radius: 8px; background: linear-gradient(90deg, #f5f7f4 0%, rgba(245,247,244,.08) 37%, rgba(22,44,36,.03) 100%), url('https://images.unsplash.com/photo-1555881400-74d7acaacd8b?auto=format&fit=crop&w=1100&q=82') center 48% / cover; } +.image-caption { position: absolute; right: 14px; bottom: 14px; display: inline-flex; align-items: center; gap: 8px; padding: 7px 11px; color: white; background: rgba(23,43,36,.74); border: 1px solid rgba(255,255,255,.26); border-radius: 4px; font-size: 10px; font-weight: 600; } +.caption-dot { background: #f6bd76; } -h1, -h2 { - font-family: var(--heading); - font-weight: 500; - color: var(--text-h); -} +.search-form { position: relative; z-index: 2; display: flex; gap: 10px; max-width: 785px; margin: 0 0 42px; padding: 8px; background: white; border: 1px solid #e2e9e3; border-radius: 7px; box-shadow: 0 8px 22px rgba(38,67,53,.06); } +.search-field { min-width: 0; min-height: 44px; flex: 1; display: flex; align-items: center; gap: 12px; padding: 0 11px; } +.search-icon { width: 21px; height: 21px; flex: 0 0 auto; color: var(--green); } +.search-field input { width: 100%; min-width: 0; height: 42px; border: 0; outline: 0; color: var(--ink); background: transparent; font-size: 13px; } +.search-field input::placeholder { color: #929b96; opacity: 1; } +.search-field:focus-within { outline: 2px solid #d29657; outline-offset: 2px; border-radius: 3px; } +.search-field kbd { flex: 0 0 auto; padding: 3px 6px; border: 1px solid #e6eae7; border-radius: 3px; color: #808a85; font: 10px 'DM Sans', sans-serif; } +.search-button { flex: 0 0 auto; min-height: 44px; padding: 0 20px; border: 0; border-radius: 5px; background: var(--green); color: #fff; font-size: 12px; font-weight: 700; cursor: pointer; transition: background .2s; } +.search-button:hover { background: var(--green-dark); } -h1 { - font-size: 56px; - letter-spacing: -1.68px; - margin: 32px 0; - @media (max-width: 1024px) { - font-size: 36px; - margin: 20px 0; - } -} -h2 { - font-size: 24px; - line-height: 118%; - letter-spacing: -0.24px; - margin: 0 0 8px; - @media (max-width: 1024px) { - font-size: 20px; - } +.explore-layout { display: grid; grid-template-columns: 235px minmax(0, 1fr); gap: 42px; align-items: start; } +.filter-panel { padding: 0 22px 0 0; border-right: 1px solid var(--line); } +.filter-heading { display: flex; align-items: center; justify-content: space-between; padding-bottom: 17px; border-bottom: 1px solid var(--line); } +.filter-heading h2 { margin: 0; color: var(--ink); font: 700 17px var(--serif); } +.reset-link, .clear-filters { color: var(--green); font-size: 11px; font-weight: 600; text-decoration: none; } +.reset-link:hover, .clear-filters:hover { text-decoration: underline; } +.filter-group { min-width: 0; margin: 0; padding: 20px 0 17px; border: 0; border-bottom: 1px solid var(--line); } +.filter-group legend { margin-bottom: 14px; padding: 0; color: #31433b; font-size: 12px; font-weight: 700; } +.check-option { position: relative; min-height: 31px; display: flex; align-items: center; gap: 9px; color: #53615b; font-size: 11px; cursor: pointer; } +.check-option input { position: absolute; width: 17px; height: 17px; margin: 0; opacity: 0; cursor: pointer; } +.checkmark { width: 16px; height: 16px; display: grid; flex: 0 0 auto; place-items: center; border: 1px solid #cbd5ce; border-radius: 3px; background: white; } +.check-option input:checked + .checkmark { border-color: var(--green); background: var(--green); } +.check-option input:checked + .checkmark::after { content: ''; width: 7px; height: 4px; margin-top: -2px; border: solid white; border-width: 0 0 1.5px 1.5px; transform: rotate(-45deg); } +.check-option input:focus-visible + .checkmark { outline: 3px solid #e29444; outline-offset: 2px; } +.option-count { margin-left: auto; color: #929c96; font-size: 10px; font-variant-numeric: tabular-nums; } +.population-group { padding-bottom: 20px; } +.population-group legend { margin-bottom: 8px; } +.select-label { display: block; margin-bottom: 7px; color: #758079; font-size: 10px; } +.filter-group select, .sort-control select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='m3 4.5 3 3 3-3' stroke='%23697671' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; } +.filter-group select { width: 100%; height: 35px; padding: 0 29px 0 10px; border: 1px solid #dce4de; border-radius: 4px; color: #52615a; background-color: white; font-size: 10px; } +.last-filter-group { padding: 20px 0 15px; } +.last-filter-group legend { margin-bottom: 5px; } +.map-promo { display: flex; align-items: center; gap: 11px; margin-top: 23px; padding: 13px 11px; border: 1px solid #e0e8e1; border-radius: 5px; background: #edf3ee; text-decoration: none; transition: border-color .2s, transform .2s; } +.map-promo:hover { border-color: #a8c2b1; transform: translateY(-1px); } +.map-promo-icon { display: grid; width: 32px; height: 32px; flex: 0 0 auto; place-items: center; border-radius: 4px; background: #dce9df; color: var(--green); } +.map-promo-icon svg { width: 19px; height: 19px; } +.map-promo-text { display: grid; gap: 3px; } +.map-promo-text strong { color: #2d4539; font-size: 10px; } +.map-promo-text span { color: #75827b; font-size: 9px; } +.map-arrow { margin-left: auto; color: var(--green); font-size: 16px; } + +.results-section { min-width: 0; } +.results-topline { min-height: 52px; display: flex; align-items: end; justify-content: space-between; gap: 20px; } +.results-kicker { margin-bottom: 7px; font-size: 9px; letter-spacing: 1.2px; } +.results-topline h2 { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0; color: var(--ink); font: 700 20px/1.25 var(--serif); letter-spacing: 0; } +.result-count { padding: 4px 7px; border-radius: 3px; background: #e9f1ec; color: #4b6a5b; font: 600 9px 'DM Sans', sans-serif; } +.sort-control { display: flex; align-items: center; gap: 9px; color: #87918b; font-size: 10px; white-space: nowrap; } +.sort-control select { height: 33px; min-width: 119px; padding: 0 26px 0 10px; border: 1px solid #e1e6e2; border-radius: 4px; color: #394b42; background-color: white; font-size: 10px; } +.active-filters { min-height: 50px; display: flex; align-items: center; gap: 10px; } +.filter-chip { display: inline-flex; align-items: center; gap: 8px; padding: 5px 8px; border: 1px solid #d8e4dc; border-radius: 3px; background: #edf3ee; color: #42614f; font-size: 10px; } +.filter-chip span { color: #658270; font-size: 14px; line-height: 10px; } + +.city-list { display: grid; gap: 13px; margin: 0; padding: 0; list-style: none; } +.city-card { min-height: 174px; display: grid; grid-template-columns: 198px minmax(0, 1fr); overflow: hidden; border: 1px solid #e5e9e5; border-radius: 6px; background: white; transition: border-color .2s, box-shadow .2s; } +.city-card:hover { border-color: #c8d7cd; box-shadow: 0 7px 18px rgba(38,67,53,.06); } +.city-image { width: 100%; height: 100%; min-height: 174px; object-fit: cover; background: #e5ebe6; } +.city-content { min-width: 0; padding: 15px 17px 12px 19px; } +.city-title-row { display: flex; align-items: start; justify-content: space-between; gap: 12px; } +.city-country { margin-bottom: 4px; color: #738078; font-size: 8px; font-weight: 700; letter-spacing: 1.05px; } +.city-country span { padding: 0 3px; color: #d09a5e; } +.city-title-row h3 { margin: 0; color: #20342b; font: 700 19px/1.2 var(--serif); letter-spacing: 0; } +.save-button { width: 30px; height: 30px; display: grid; flex: 0 0 auto; place-items: center; border: 1px solid #e5eae6; border-radius: 4px; background: white; color: #75827b; cursor: pointer; transition: color .2s, border-color .2s; } +.save-button:hover { color: var(--green); border-color: #9cb5a5; } +.save-button svg { width: 17px; height: 17px; } +.city-description { margin: 10px 0 12px; color: #68756e; font-size: 10px; line-height: 1.5; } +.city-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; color: #67746d; font-size: 9px; font-variant-numeric: tabular-nums; } +.rating { color: #314a3c; font-weight: 700; } +.rating > span { color: #e7a44e; } +.rating-count { color: #8a948e; font-weight: 400; } +.meta-divider { width: 3px; height: 3px; border-radius: 50%; background: #bbc5bd; } +.details-link { display: inline-flex; align-items: center; gap: 7px; margin-top: 12px; color: var(--green); font-size: 10px; font-weight: 700; text-decoration: none; } +.details-link span, .map-link span { transition: transform .2s; } +.details-link:hover { text-decoration: underline; } +.details-link:hover span { transform: translateX(3px); } +.results-footer { display: flex; align-items: center; justify-content: space-between; padding: 17px 2px 0; color: #88928c; font-size: 10px; } +.results-footer a { display: inline-flex; align-items: center; gap: 7px; color: var(--green); font-weight: 700; text-decoration: none; } +.results-footer a:hover { text-decoration: underline; } + +.map-preview { min-height: 235px; display: grid; grid-template-columns: .85fr 1.15fr; align-items: center; gap: 30px; margin-top: 58px; padding: 28px 32px; overflow: hidden; border-radius: 7px; background: #eaf1eb; } +.map-preview-copy .results-kicker { margin-bottom: 10px; } +.map-preview-copy h2 { max-width: 350px; margin-bottom: 8px; color: #20372c; font: 700 22px/1.25 var(--serif); } +.map-preview-copy > p:not(.results-kicker) { margin-bottom: 15px; color: #6a786f; font-size: 11px; line-height: 1.5; } +.map-link { display: inline-flex; align-items: center; gap: 8px; color: var(--green-dark); font-size: 11px; font-weight: 700; text-decoration: none; } +.map-link:hover { text-decoration: underline; } +.map-link:hover span { transform: translate(2px,-2px); } +.map-visual { position: relative; height: 176px; overflow: hidden; background-image: radial-gradient(#b9cabe 0.8px, transparent .8px); background-position: 0 0; background-size: 12px 12px; } +.map-land { position: absolute; display: block; background: #b8ccb9; opacity: .88; filter: drop-shadow(0 2px 2px rgba(81,119,92,.08)); } +.land-one { top: 19%; left: 8%; width: 28%; height: 47%; border-radius: 55% 32% 49% 39% / 31% 40% 57% 55%; transform: rotate(-15deg); clip-path: polygon(0 20%,22% 0,48% 12%,63% 4%,100% 23%,82% 47%,91% 66%,64% 100%,42% 75%,18% 82%,4% 56%); } +.land-two { top: 21%; left: 37%; width: 16%; height: 30%; border-radius: 42% 58% 35% 65%; transform: rotate(9deg); clip-path: polygon(9% 12%,44% 0,71% 15%,100% 44%,73% 72%,62% 100%,39% 84%,22% 64%,0 46%); } +.land-three { top: 22%; right: 8%; width: 38%; height: 48%; border-radius: 50% 47% 35% 55% / 36% 40% 59% 51%; transform: rotate(7deg); clip-path: polygon(5% 18%,30% 7%,52% 0,83% 12%,100% 31%,88% 57%,65% 49%,58% 81%,39% 100%,23% 79%,18% 52%,0 42%); } +.land-four { bottom: 3%; left: 45%; width: 16%; height: 31%; border-radius: 44% 50% 59% 43%; transform: rotate(-14deg); clip-path: polygon(15% 0,74% 9%,100% 38%,76% 66%,64% 100%,42% 80%,33% 52%,0 33%); } +.map-pin { position: absolute; z-index: 1; width: 10px; height: 10px; border: 2px solid white; border-radius: 50%; background: #df9251; box-shadow: 0 1px 5px rgba(39,69,49,.28); } +.pin-one { top: 39%; left: 27%; }.pin-two { top: 31%; left: 48%; }.pin-three { top: 45%; left: 55%; }.pin-four { top: 37%; right: 22%; }.pin-five { bottom: 22%; left: 49%; } +.site-footer { min-height: 83px; display: flex; align-items: center; gap: 17px; margin-top: 25px; border-top: 1px solid var(--line); } +.footer-brand { font-size: 16px; } +.site-footer p { margin: 0; color: #87928b; font-size: 10px; } +.footer-note { margin-left: auto; color: #87928b; font-size: 10px; } + +@media (min-width: 1440px) { .page-shell { width: min(1184px, calc(100% - 96px)); } } +@media (max-width: 820px) { + .site-header { height: 68px; padding: 0 24px; } + .main-nav { gap: 23px; margin-left: 0; } + .page-shell { width: calc(100% - 48px); } + .intro { min-height: 220px; } + .intro-image { width: 48%; opacity: .88; } + .explore-layout { grid-template-columns: 195px minmax(0,1fr); gap: 25px; } + .filter-panel { padding-right: 16px; } + .city-card { grid-template-columns: 150px minmax(0,1fr); } + .city-content { padding: 13px 12px; } + .city-meta { gap: 6px; } + .city-description { margin: 8px 0 10px; } + .map-preview { padding: 25px; gap: 16px; } } -p { - margin: 0; +@media (max-width: 620px) { + .site-header { height: 62px; padding: 0 18px; } + .brand { gap: 6px; font-size: 20px; } + .brand-mark, .brand-mark svg { width: 29px; height: 29px; } + .main-nav { gap: 16px; } + .nav-link { font-size: 11px; } + .login-link { width: 34px; height: 34px; justify-content: center; padding: 0; font-size: 0; } + .login-icon, .login-icon svg { width: 19px; height: 19px; } + .page-shell { width: calc(100% - 36px); } + .intro { min-height: 246px; align-items: end; margin-top: 18px; } + .intro-copy { padding: 0 0 22px; } + .intro-image { top: 0; right: -18px; width: 78%; height: 164px; border-radius: 5px 0 0 5px; opacity: .72; } + .intro-image::after { content: ''; position: absolute; inset: 0; background: linear-gradient(90deg, #f5f7f4 0%, rgba(245,247,244,.1) 72%); } + .image-caption { z-index: 1; right: 28px; bottom: 10px; padding: 5px 8px; font-size: 9px; } + .eyebrow { margin-bottom: 8px; font-size: 8px; letter-spacing: 1.1px; } + h1 { margin-bottom: 7px; font-size: 34px; } + .intro-description { max-width: 310px; font-size: 11px; } + .search-form { gap: 5px; margin-bottom: 31px; padding: 6px; } + .search-field { gap: 8px; padding: 0 6px; } + .search-field input { font-size: 11px; } + .search-field input::placeholder { text-overflow: ellipsis; } + .search-field kbd { display: none; } + .search-button { min-height: 40px; padding: 0 11px; font-size: 10px; } + .explore-layout { display: flex; flex-direction: column; gap: 26px; } + .filter-panel { width: 100%; padding: 0; border-right: 0; } + .filter-heading { padding-bottom: 12px; } + .filter-group { padding: 14px 0 11px; } + .filter-group legend { margin-bottom: 9px; } + .filter-group:not(.population-group):not(.last-filter-group) { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); column-gap: 12px; } + .filter-group:not(.population-group):not(.last-filter-group) legend { grid-column: 1 / -1; } + .check-option { min-height: 29px; } + .population-group { display: inline-block; width: calc(62% - 8px); vertical-align: top; border: 0; } + .last-filter-group { display: inline-block; width: 38%; vertical-align: top; border: 0; padding-top: 14px; } + .last-filter-group .check-option { align-items: start; } + .last-filter-group .option-count { display: none; } + .filter-group select { height: 33px; } + .map-promo { display: none; } + .results-topline { align-items: start; } + .results-topline h2 { gap: 7px; font-size: 18px; } + .sort-control { flex-direction: column; align-items: end; gap: 4px; font-size: 9px; } + .sort-control select { min-width: 105px; height: 30px; font-size: 9px; } + .active-filters { min-height: 43px; } + .city-list { gap: 11px; } + .city-card { grid-template-columns: 112px minmax(0,1fr); min-height: 158px; } + .city-image { min-height: 158px; } + .city-content { padding: 11px 10px; } + .city-title-row h3 { font-size: 16px; } + .city-country { font-size: 7px; letter-spacing: .7px; } + .save-button { width: 26px; height: 26px; } + .city-description { margin: 7px 0 8px; font-size: 9px; } + .city-meta { column-gap: 5px; row-gap: 4px; font-size: 8px; } + .meta-divider { width: 2px; height: 2px; } + .details-link { margin-top: 9px; font-size: 9px; } + .results-footer { font-size: 9px; } + .map-preview { min-height: 0; grid-template-columns: 1fr; gap: 12px; margin-top: 38px; padding: 20px; } + .map-preview-copy h2 { font-size: 19px; } + .map-preview-copy > p:not(.results-kicker) { margin-bottom: 10px; } + .map-visual { height: 130px; } + .site-footer { min-height: 68px; gap: 10px; } + .site-footer p, .footer-note { font-size: 8px; } } - -code, -.counter { - font-family: var(--mono); - display: inline-flex; - border-radius: 4px; - color: var(--text-h); +@media (max-width: 390px) { + .main-nav { gap: 11px; } + .nav-link { font-size: 10px; } + .city-card { grid-template-columns: 94px minmax(0,1fr); } + .city-content { padding: 9px 8px; } + .city-description { line-height: 1.35; } + .city-meta { font-size: 7px; } + .sort-control { max-width: 100px; } } - -code { - font-size: 15px; - line-height: 135%; - padding: 4px 8px; - background: var(--code-bg); +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; } } From 0f790a004c213bf5b0aea454bf7981cd1e15af2c Mon Sep 17 00:00:00 2001 From: Wilhelm Ringvold Date: Tue, 6 Oct 2026 11:28:23 +0200 Subject: [PATCH 2/2] lagt til bokmerke funksjon --- t20-project-2/index.html | 165 +------------ t20-project-2/src/App.tsx | 468 +++++++++++++++++++++++++++--------- t20-project-2/src/index.css | 11 + 3 files changed, 371 insertions(+), 273 deletions(-) diff --git a/t20-project-2/index.html b/t20-project-2/index.html index 0a84d47..839219a 100644 --- a/t20-project-2/index.html +++ b/t20-project-2/index.html @@ -8,171 +8,10 @@ content="Discover cities around the world with Country Boys, a searchable world city catalog." /> - Country Boys – World City Catalog - - -
-
-
-

THE WORLD IS CLOSER THAN YOU THINK

-

Find the city that
speaks to you.

-

From quiet coastal towns to lively capitals. Your next discovery starts here.

-
- -
- - - -
- - -
-
-
-

YOUR NEXT DESTINATION

-

Cities to explore 24,080

-
- -
- -
- Europe - Clear all -
- -
    -
  1. - Colorful houses along Nyhavn in Copenhagen -
    -

    DENMARK · EUROPE

    Copenhagen

    -

    Bike-friendly streets, Nordic design, and long summer evenings by the water.

    -
    4.8 (248)653,000 residents14 m above sea level
    - View city details -
    -
  2. - -
  3. - Yellow tram on a street in Lisbon -
    -

    PORTUGAL · EUROPE

    Lisbon

    -

    Pastel facades, steep hills, and the salty Atlantic breeze.

    -
    4.9 (391)545,000 residents2 m above sea level
    - View city details -
    -
  4. - -
  5. - Traditional Kyoto street with a pagoda in the background -
    -

    JAPAN · ASIA

    Kyoto

    -

    Timber houses, peaceful temple gardens, and a city in constant motion.

    -
    4.7 (184)1.46 million residents50 m above sea level
    - View city details -
    -
  6. -
- - -
-
- -
-

SEE THE WORLD FROM A NEW PERSPECTIVE

Where will curiosity take you?

Every pin is a city with a story of its own. Find it on the map.

Open the world map
- -
- - -
+
+ diff --git a/t20-project-2/src/App.tsx b/t20-project-2/src/App.tsx index 9ca8e8d..b9a73a1 100644 --- a/t20-project-2/src/App.tsx +++ b/t20-project-2/src/App.tsx @@ -1,122 +1,370 @@ -import { useState } from 'react' -import heroImg from './assets/hero.png' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import './App.css' +import { useEffect, useState } from 'react' -function App() { - const [count, setCount] = useState(0) +type City = { + id: string + name: string + country: string + continent: string + description: string + rating: string + reviews: number + population: string + elevation: string + image: string + imageAlt: string +} + +const cities: City[] = [ + { + id: 'copenhagen', + name: 'Copenhagen', + country: 'Denmark', + continent: 'Europe', + description: 'Bike-friendly streets, Nordic design, and long summer evenings by the water.', + rating: '4.8', + reviews: 248, + population: '653,000 residents', + elevation: '14 m above sea level', + image: + 'https://images.unsplash.com/photo-1513622470522-26c3c8a854bc?auto=format&fit=crop&w=420&q=78', + imageAlt: 'Colorful houses along Nyhavn in Copenhagen', + }, + { + id: 'lisbon', + name: 'Lisbon', + country: 'Portugal', + continent: 'Europe', + description: 'Pastel facades, steep hills, and the salty Atlantic breeze.', + rating: '4.9', + reviews: 391, + population: '545,000 residents', + elevation: '2 m above sea level', + image: + 'https://images.unsplash.com/photo-1555881400-74d7acaacd8b?auto=format&fit=crop&w=420&q=78', + imageAlt: 'Yellow tram on a street in Lisbon', + }, + { + id: 'kyoto', + name: 'Kyoto', + country: 'Japan', + continent: 'Asia', + description: 'Timber houses, peaceful temple gardens, and a city in constant motion.', + rating: '4.7', + reviews: 184, + population: '1.46 million residents', + elevation: '50 m above sea level', + image: + 'https://images.unsplash.com/photo-1493976040374-85c8e12f0c0e?auto=format&fit=crop&w=420&q=78', + imageAlt: 'Traditional Kyoto street with a pagoda in the background', + }, +] + +const storageKey = 'country-boys:saved-cities' + +function readSavedCities(): string[] { + try { + const stored = window.localStorage.getItem(storageKey) + const parsed: unknown = stored ? JSON.parse(stored) : [] + if (!Array.isArray(parsed)) return [] + + return parsed.filter( + (id): id is string => + typeof id === 'string' && cities.some((city) => city.id === id), + ) + } catch { + return [] + } +} + +function BookmarkIcon({ filled }: { filled: boolean }) { return ( - <> -
-
- - React logo - Vite logo -
-
-

Get started

-

- Edit src/App.tsx and save to test HMR -

+ + ) +} + +function CityCard({ + city, + saved, + onToggleSaved, +}: { + city: City + saved: boolean + onToggleSaved: (cityId: string) => void +}) { + return ( +
  • + {city.imageAlt} +
    +
    +
    +

    + {city.country} · {city.continent} +

    +

    {city.name}

    +
    +
    - -
  • - -
    - -
    -
    - -

    Documentation

    -

    Your questions, answered

    - +

    {city.description}

    +
    + + {city.rating}{' '} + ({city.reviews}) + + + {city.population} + + {city.elevation}
    -
    - -

    Connect with us

    -

    Join the Vite community

    -
    + + ) +} + +function App() { + const [savedCities, setSavedCities] = useState(readSavedCities) + const [activeView, setActiveView] = useState<'home' | 'saved'>('home') + + useEffect(() => { + try { + window.localStorage.setItem(storageKey, JSON.stringify(savedCities)) + } catch { + // Bookmarks remain available for this session if browser storage is disabled. + } + }, [savedCities]) + + const toggleSavedCity = (cityId: string) => { + setSavedCities((current) => + current.includes(cityId) + ? current.filter((savedId) => savedId !== cityId) + : [...current, cityId], + ) + } + + const visibleCities = + activeView === 'saved' + ? cities.filter((city) => savedCities.includes(city.id)) + : cities + const isSavedView = activeView === 'saved' + + return ( + <> +
    + + + Country Boys. + + + + + + + Log in + +
    + +
    +
    +
    +

    THE WORLD IS CLOSER THAN YOU THINK

    +

    Find the city that
    speaks to you.

    +

    From quiet coastal towns to lively capitals. Your next discovery starts here.

    +
    +
    + Lisbon, Portugal +
    +
    + +
    + + +
    + +
    + {!isSavedView && ( + + )} + +
    +
    +
    +

    {isSavedView ? 'YOUR BOOKMARKS' : 'YOUR NEXT DESTINATION'}

    +

    + {isSavedView ? 'Saved cities' : 'Cities to explore'}{' '} + {isSavedView ? savedCities.length : '24,080'} +

    +
    + {!isSavedView && ( + + )} +
    + + {!isSavedView && ( +
    + Europe + Clear all +
    + )} + + {visibleCities.length > 0 ? ( +
      + {visibleCities.map((city) => ( + + ))} +
    + ) : ( +
    + +

    No saved cities yet

    +

    Save a city with the bookmark icon and it will appear here.

    + +
    + )} + + {visibleCities.length > 0 && ( +
    + {isSavedView ? `${visibleCities.length} saved ${visibleCities.length === 1 ? 'city' : 'cities'}` : 'Showing 3 of 24,080 cities'} + {!isSavedView && Show more cities } +
    + )} +
    -
    -
    -
    +
    +
    +

    SEE THE WORLD FROM A NEW PERSPECTIVE

    +

    Where will curiosity take you?

    +

    Every pin is a city with a story of its own. Find it on the map.

    + Open the world map +
    +
    + + + + + +
    +
    + + + ) } -export default App +export default App \ No newline at end of file diff --git a/t20-project-2/src/index.css b/t20-project-2/src/index.css index a7c9215..9afc521 100644 --- a/t20-project-2/src/index.css +++ b/t20-project-2/src/index.css @@ -44,9 +44,11 @@ button, a, input, select { -webkit-tap-highlight-color: transparent; } .brand-period, .footer-brand span { color: #e29a4d; } .main-nav { height: 100%; display: flex; align-items: center; gap: 38px; margin-left: -28px; } .nav-link { position: relative; height: 100%; display: inline-flex; align-items: center; color: #66716c; font-size: 14px; text-decoration: none; } +.main-nav button { padding: 0; border: 0; background: transparent; cursor: pointer; } .nav-link:hover, .nav-link-active { color: var(--green-dark); } .nav-link-active { font-weight: 700; } .nav-link-active::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background: var(--green); } +.saved-count { min-width: 17px; height: 17px; display: inline-grid; place-items: center; margin-left: 6px; padding: 0 4px; border-radius: 9px; background: #e9f1ec; color: var(--green-dark); font-size: 9px; font-weight: 700; } .login-link { display: inline-flex; align-items: center; gap: 8px; padding: 10px 15px; border: 1px solid #dce4df; border-radius: 6px; background: #fff; color: var(--ink); text-decoration: none; font-size: 13px; font-weight: 600; transition: border-color .2s, background-color .2s; } .login-link:hover { border-color: #a7bdb1; background: #f8faf8; } .login-icon, .login-icon svg { display: block; width: 18px; height: 18px; } @@ -127,6 +129,7 @@ h1 span { color: var(--green); } .city-title-row h3 { margin: 0; color: #20342b; font: 700 19px/1.2 var(--serif); letter-spacing: 0; } .save-button { width: 30px; height: 30px; display: grid; flex: 0 0 auto; place-items: center; border: 1px solid #e5eae6; border-radius: 4px; background: white; color: #75827b; cursor: pointer; transition: color .2s, border-color .2s; } .save-button:hover { color: var(--green); border-color: #9cb5a5; } +.save-button[aria-pressed="true"] { color: var(--green); border-color: #c6d8cb; background: #edf4ef; } .save-button svg { width: 17px; height: 17px; } .city-description { margin: 10px 0 12px; color: #68756e; font-size: 10px; line-height: 1.5; } .city-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; color: #67746d; font-size: 9px; font-variant-numeric: tabular-nums; } @@ -141,6 +144,14 @@ h1 span { color: var(--green); } .results-footer { display: flex; align-items: center; justify-content: space-between; padding: 17px 2px 0; color: #88928c; font-size: 10px; } .results-footer a { display: inline-flex; align-items: center; gap: 7px; color: var(--green); font-weight: 700; text-decoration: none; } .results-footer a:hover { text-decoration: underline; } +.saved-view { grid-template-columns: minmax(0, 1fr); } +.empty-state { display: grid; justify-items: center; padding: 42px 20px; border: 1px dashed #d5dfd7; border-radius: 6px; background: rgba(255,255,255,.55); text-align: center; } +.empty-bookmark { width: 40px; height: 40px; display: grid; place-items: center; margin-bottom: 12px; border-radius: 50%; background: #e9f1ec; color: var(--green); } +.empty-bookmark svg { width: 20px; height: 20px; } +.empty-state h3 { margin: 0 0 5px; color: var(--ink); font: 700 16px var(--serif); } +.empty-state p { margin: 0 0 15px; color: var(--muted); font-size: 11px; } +.empty-state button { padding: 9px 13px; border: 0; border-radius: 4px; background: var(--green); color: white; font-size: 11px; font-weight: 700; cursor: pointer; } +.empty-state button:hover { background: var(--green-dark); } .map-preview { min-height: 235px; display: grid; grid-template-columns: .85fr 1.15fr; align-items: center; gap: 30px; margin-top: 58px; padding: 28px 32px; overflow: hidden; border-radius: 7px; background: #eaf1eb; } .map-preview-copy .results-kicker { margin-bottom: 10px; }