Sidopanelens teknik

Uppdaterad dokumentation: Denna sida innehåller detaljerad information om hur sidopanelen och huvudinnehållet samverkar, AJAX-teknik för dynamisk innehållsladdning, och tips för bildcollage-hantering.

Gamla tekniken

Den ursprungliga hemsidan använde HTML frames för att dela upp sidan i en sidopanel och ett huvudinnehåll. Detta var en vanlig teknik på 90-talet, men har sedan dess blivit föråldrad av flera anledningar:

  • Frames är inte längre stödda i HTML5
  • Frames kan orsaka problem med sökmotorer då varje frame indexeras separat
  • Frames kan vara svåra att hantera responsivt för mobila enheter
  • Frames kan orsaka problem med bokmärken, delning av länkar och navigationshistorik

Här är ett exempel på hur den gamla tekniken såg ut:

<frameset cols="200,*">
    <frame src="sidebar.html" name="sidebar">
    <frame src="main.html" name="main">
</frameset>
                    
Besök den gamla hemsidan

Nya tekniken

Den nya hemsidan använder moderna webtekniker för att uppnå samma funktionalitet som frames gav, men på ett mer modernt och flexibelt sätt:

  • HTML5 semantisk struktur med <nav> och <main> element för bättre tillgänglighet och SEO
  • CSS för layout och responsiv design som anpassar sig till olika skärmstorlekar
  • JavaScript för dynamisk innehållsladdning och interaktivitet
  • AJAX för att ladda innehåll utan att ladda om hela sidan, vilket ger en smidigare användarupplevelse

Här är ett exempel på den nya strukturen:

<body>
    <div class="menu-toggle">☰</div>
    <div class="overlay"></div>
    
    <nav id="sidebar">
        <!-- Sidopanelsinnehåll -->
    </nav>
    
    <main>
        <!-- Huvudinnehåll -->
    </main>
    
    <script src="script.js"></script>
</body>
                    

Förhållandet mellan sidopanel och huvudinnehåll

På moderna webbplatser används vanligtvis två huvudkomponenter för layout: sidopanelen (sidebar) och huvudinnehållet (main content).

Sidopanelen (Sidebar)

Sidopanelen är den fasta navigationssektion som oftast finns på vänster sida av skärmen. Den innehåller:

  • Navigationsmenyer
  • Länkar till viktiga sektioner
  • Tillbaka-till-startsida länkar
  • Ibland även kontaktinformation eller sociala medier-länkar

I HTML representeras den av <nav id="sidebar"> elementet.

Huvudinnehållet (Main Content)

Huvudinnehållet är det dynamiska området där det primära innehållet visas. Det kan vara:

  • Text och artiklar
  • Bildgallerier
  • Formulär
  • Dynamiskt innehåll som laddas in via AJAX

I HTML representeras det av <main> elementet.

Hur de samverkar

Förhållandet mellan sidopanelen och huvudinnehållet är nyckeln till en välfungerande webbplats:

  • Fast vs. Dynamisk: Sidopanelen förblir oftast stationär medan huvudinnehållet uppdateras dynamiskt när användaren navigerar.
  • Kontextuell navigering: Sidopanelen anpassar sina länkar baserat på var i hierarkin användaren befinner sig.
  • Responsivitet: På mobila enheter döljs sidopanelen ofta och kan visas vid behov genom en menyknapp.
  • Samordnad uppdatering: När innehållet i huvuddelen ändras måste sidopanelens länkar anpassas för att undvika brutna länkar.

Viktigt att tänka på: När man implementerar en sidopanel och huvudinnehåll utan frames krävs JavaScript för att upprätthålla riktig navigering mellan sidorna och för att hantera webbläsarens historik (tillbaka-knapp).

Kodexempel: Grundläggande struktur

<!-- HTML struktur -->
<div class="container">
    <nav id="sidebar">
        <h2><a href="/">Tillbaka till hemsidan</a></h2>
        <div class="nav-section">
            <h3>Navigering</h3>
            <ul>
                <li><a href="sida1.html">Sida 1</a></li>
                <li><a href="sida2.html">Sida 2</a></li>
            </ul>
        </div>
    </nav>
    
    <main>
        <!-- Huvudinnehåll som laddas dynamiskt -->
    </main>
</div>

<!-- CSS för layout -->
<style>
.container {
    display: flex;
}

#sidebar {
    width: 250px;
    position: fixed;
    height: 100vh;
    overflow-y: auto;
    background-color: #f5f5f5;
}

main {
    margin-left: 250px;
    flex: 1;
    padding: 20px;
}

@media (max-width: 768px) {
    #sidebar {
        transform: translateX(-100%);
    }
    
    main {
        margin-left: 0;
    }
}
</style>
                    

AJAX-teknik för dynamisk innehållsladdning

AJAX (Asynchronous JavaScript And XML) är en teknik som möjliggör att webbsidor kan uppdateras asynkront genom att utbyta data med en server i bakgrunden. Detta innebär att webbsidor kan uppdatera delar av sidan utan att behöva ladda om hela sidan.

AJAX-diagram

Hur AJAX fungerar i vår implementation

I vår implementation av sidopanelen och huvudinnehållet använder vi AJAX för att:

  1. Fånga länkklick i sidopanelen
  2. Förhindra standardnavigeringen genom event.preventDefault()
  3. Göra ett AJAX-anrop för att hämta innehållet från den länkade sidan
  4. Extrahera main-innehållet från det hämtade HTML-dokumentet
  5. Uppdatera huvudinnehållet med det nya innehållet utan att ladda om hela sidan
  6. Uppdatera URL:en med history.pushState() för att bibehålla navigeringshistoriken

Steg för steg: AJAX-processen

1. Fånga länkklicket

link.addEventListener('click', function(event) {
    // Förhindra standardnavigeringen
    event.preventDefault();
    
    // Hämta URL:en från länken
    const url = this.getAttribute('href');
    
    // Ladda innehållet
    loadContent(url);
});
                            

2. Ladda innehållet med fetch API

function loadContent(url) {
    // Visa laddningsindikator
    mainContent.innerHTML = 
        '<div class="loading-indicator">' + 
        'Laddar innehåll...</div>';
    
    // Hämta innehållet med fetch
    fetch(url)
        .then(response => response.text())
        .then(html => {
            // Hantera innehållet här
        })
        .catch(error => {
            // Visa felmeddelande
        });
}
                            

3. Extrahera och uppdatera innehållet

.then(html => {
    // Skapa en temporär div för att parsa HTML
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    
    // Extrahera main-innehållet
    const newContent = doc.querySelector('main');
    
    // Uppdatera huvudinnehållet
    if (newContent) {
        mainContent.innerHTML = newContent.innerHTML;
    } else {
        mainContent.innerHTML = doc.body.innerHTML;
    }
    
    // Uppdatera URL:en utan omladdning
    history.pushState({}, '', url);
    
    // Uppdatera sidopanellänkar
    updateSidebarLinks(url);
});
                            

4. Hantera navigeringshistorik

// Hantera webbläsarens bakåt/framåt-knappar
window.addEventListener('popstate', function() {
    // Om vi navigerar till startsidan
    if (location.pathname === '/' || 
        location.pathname === '/index.html') {
        mainContent.innerHTML = originalContent;
        return;
    }
    
    // För andra sidor, ladda innehållet
    loadContent(location.pathname);
});
                            

Fördelar med AJAX-tekniken

  • Snabbare användarupplevelse - endast det nödvändiga innehållet laddas, inte hela sidan
  • Smidigare övergångar - ingen fullständig omladdning av sidan vilket ger en mer applikationsliknande känsla
  • Minskad serverbörda - bara delar av innehållet behöver skickas, inte hela HTML-strukturen
  • Bibehållen sidopanel - sidopanelen förblir synlig och behöver inte laddas om för varje navigation

Utmaningar med AJAX-tekniken

  • Komplexare JavaScript - kräver mer kodning för att hantera navigering, historik och länkhantering
  • Korrekt URL-hantering - måste använda history.pushState() för att uppdatera URL och möjliggöra bokmärken
  • Relativa sökvägar - måste hantera relativa sökvägar i både HTML och CSS för innehåll som laddas dynamiskt
  • Hantering av script - skript i dynamiskt laddade sidor måste utvärderas explicit
Viktigt att veta: När innehåll laddas via AJAX behöver eventuella skript i det nya innehållet köras manuellt. I vår implementation kallas handleImageGalleryLinks() efter att innehållet har laddats för att säkerställa att bildlänkar fungerar korrekt.

Länkhantering

En av de mest komplexa aspekterna av en dynamisk webbplats är hur länkarna hanteras mellan sidopanelen och huvudinnehållet. Särskilt viktig är hanteringen av relativa sökvägar när användaren navigerar mellan olika nivåer i webbplatsens hierarki.

Olika typer av länkar

  • Absoluta sökvägar (börjar med /) - pekar från roten av webbplatsen, t.ex. /intyg/certifikat.pdf
  • Relativa sökvägar (t.ex. elin.html eller ./bilder/foto.jpg) - pekar relativt till aktuell sida
  • Externa länkar (http://... eller https://...) - öppnas normalt i nytt fönster/flik
  • Specialfiler (PDF, DOC, GIF, etc.) - kan ha särskild hantering

Intelligenta beslut om länköppning

För att bestämma hur en länk ska hanteras använder vi funktionen shouldOpenInNewWindow() som analyserar länkens egenskaper:

function shouldOpenInNewWindow(link) {
    const url = link.getAttribute('href');
    if (!url) return false;
    
    const isExternalLink = url.startsWith('http://') || url.startsWith('https://');
    const isSpecialFile = url.toLowerCase().endsWith('.pdf') || 
                         url.toLowerCase().endsWith('.doc') || 
                         url.toLowerCase().endsWith('.docx') || 
                         url.toLowerCase().endsWith('.xlsx') || 
                         url.toLowerCase().endsWith('.zip');
    const hasTargetBlank = link.hasAttribute('target') && link.getAttribute('target') === '_blank';
    
    // Kontrollera om det är en bildgalleri-sida
    const isGalleryPage = isImageGalleryPage(url);
    
    // Kontrollera om det är en GIF-fil som länkas direkt från sidopanelen
    const isDirectGifLink = url.toLowerCase().endsWith('.gif') && 
                           link.closest('#sidebar') !== null;
    
    // Öppna i nytt fönster endast om det är en extern länk, specialfil eller explicit target="_blank"
    // och INTE om det är en bildgalleri-sida eller en direkt GIF-länk från sidopanelen
    return (isExternalLink || isSpecialFile || hasTargetBlank) && !isGalleryPage && !isDirectGifLink;
}
                    

Hantering av relativa sökvägar

När användaren navigerar mellan olika nivåer av webbplatsen (t.ex. från startsidan till /intyg/ och tillbaka) behöver länkarna i sidopanelen uppdateras för att reflektera den nya platsen. Detta hanteras genom:

function updateSidebarLinks(currentPath) {
    // Ta bort inledande och avslutande snedstreck för att få en ren sökväg
    const cleanPath = currentPath ? currentPath.replace(/^\/+|\/+$/g, '') : '';
    
    // Om vi är i roten, återställ länkarna till originalstatus
    if (!cleanPath) {
        sidebarLinks.forEach(link => {
            const originalHref = link.getAttribute('data-original-href');
            if (originalHref) {
                link.setAttribute('href', originalHref);
            }
        });
        return;
    }
    
    // För varje länk i sidopanelen
    sidebarLinks.forEach(link => {
        const originalHref = link.getAttribute('data-original-href');
        if (!originalHref) return;
        
        // Om länken har en absolut sökväg (börjar med /), gör den relativ
        if (originalHref.startsWith('/')) {
            // Ta bort inledande snedstreck
            const relativeHref = originalHref.substring(1);
            
            // Om vi är i en undermapp, lägg till ../ för att gå tillbaka till roten
            if (cleanPath) {
                const depth = cleanPath.split('/').length;
                const backToRoot = '../'.repeat(depth);
                link.setAttribute('href', backToRoot + relativeHref);
            } else {
                link.setAttribute('href', relativeHref);
            }
        } 
        // Om länken har en relativ sökväg (börjar med ./ eller inte börjar med /)
        else if (originalHref.startsWith('./') || !originalHref.startsWith('/')) {
            // Om vi är i en undermapp, lägg till ../ för att gå tillbaka till roten
            if (cleanPath) {
                const depth = cleanPath.split('/').length;
                const backToRoot = '../'.repeat(depth);
                link.setAttribute('href', backToRoot + originalHref);
            } else {
                link.setAttribute('href', originalHref);
            }
        }
    });
}
                    

Viktiga överväganden för länkhantering

  • Spara originalvärden: Spara alltid original href för varje länk för att kunna återställa den
  • Djupberäkning: Beräkna hur många "../" som behövs baserat på aktuell sökvägsdjup
  • Olika behandling: Hantera olika länktyper (bilder, gallerisidor, dokument) på olika sätt
  • Historikhantering: Använd history API för att uppdatera URL:en och möjliggöra bakåt/framåt-navigering
  • Konsekvens: Var konsekvent i hur du hanterar olika typer av länkar över hela webbplatsen
Vanliga fallgropar vid länkhantering:
  • Att inte hantera relativa länkar korrekt när man navigerar mellan olika djup i mappstrukturen
  • Att inte spara original href-värden, vilket gör det svårt att återställa länkar
  • Att inte uppdatera URL:en med history.pushState(), vilket bryter webbläsarhistoriken
  • Att inte hantera specialfall för olika filtyper (bilder, dokument, etc.)

Bildcollage och gallerihantering

Ett bildcollage eller galleri är ett effektivt sätt att visa en samling bilder på en webbsida. På denna webbplats används bildcollagen för att visa bilder på familjemedlemmar som Elin, Erik, Lotta och Kent.

Struktur för bildcollage

En bra bildcollage-struktur i HTML och CSS innehåller följande delar:

<!-- HTML-struktur för ett bildcollage -->
<div class="gallery-container">
    <h1>Bilder på Elin</h1>
    <div class="gallery-grid">
        <div class="gallery-item">
            <img src="./bilder/elin/bild1.jpg" alt="Elin 2019">
            <div class="gallery-caption">Elin 2019</div>
        </div>
        <div class="gallery-item">
            <img src="./bilder/elin/bild2.jpg" alt="Elin 2020">
            <div class="gallery-caption">Elin 2020</div>
        </div>
        <!-- Fler bilder... -->
    </div>
</div>

<!-- CSS för bildcollage -->
<style>
.gallery-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}

.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 20px;
    padding: 20px;
}

.gallery-item {
    position: relative;
    overflow: hidden;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    transition: transform 0.3s ease;
    aspect-ratio: 1;  /* Viktigt för jämna proportioner */
}

.gallery-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;  /* Viktigt för att hantera olika bildstorlekar */
    display: block;
}

.gallery-caption {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    background: rgba(0,0,0,0.7);
    color: white;
    padding: 10px;
    opacity: 0;
    transition: opacity 0.3s ease;
}

.gallery-item:hover .gallery-caption {
    opacity: 1;
}
</style>
                    

Viktiga aspekter för bildcollage

1. Proportioner och storlek

  • Använd aspect-ratio: Säkerställer att alla bildcontainrar har samma proportioner, oavsett innehåll
  • object-fit: cover: Ser till att bilden fyller hela containern utan att förvrängas
  • Dynamiska kolumner: Använd repeat(auto-fit, minmax(300px, 1fr)) för att låta galleriet anpassa antalet kolumner efter skärmstorlek
  • Gap: Definiera mellanrum mellan bilderna för ett snyggt och luftigt utseende

2. Användarupplevelse

  • Hover-effekter: Lägg till svaga transformationer eller skuggeffekter vid hover
  • Bildtexter: Visa bildtexter endast vid hover för en renare layout
  • Mjuka övergångar: Använd transition för att skapa mjuka animationer
  • Klickbeteende: Se till att bilder öppnas i nytt fönster eller i en lightbox

Utmaningar med dynamiskt laddade gallerier

När bildgallerier laddas dynamiskt via AJAX kan vissa utmaningar uppstå:

  • CSS laddas inte: Inline-CSS i huvudet på den laddade sidan kan ignoreras
  • JavaScript för galleri körs inte automatiskt: Skript i laddade sidor måste köras explicit
  • Relativa sökvägar till bilder kan brytas: Beroende på navigationssökväg
  • Bildstorlekar kan variera: Vilket kan skapa ett ojämnt collage

Vår lösning med JavaScript

För att säkerställa att bildcollagen fungerar korrekt när de laddas dynamiskt via AJAX har vi implementerat följande lösning:

// Kontrollera om det är en bildgalleri-sida
if (isImageGalleryPage(url)) {
    console.log("Bildgalleri-sida laddad:", url);
    
    // Försök hitta .gallery-grid elementet
    const galleryGrid = mainContent.querySelector('.gallery-grid');
    console.log("Gallery grid element:", galleryGrid);
    
    if (galleryGrid) {
        // För bildgalleriet, hitta alla gallery-item element
        const galleryItems = galleryGrid.querySelectorAll('.gallery-item');
        console.log("Gallery items:", galleryItems.length);
        
        // Säkerställ att CSS för galleri laddas korrekt
        galleryGrid.style.display = 'grid';
        galleryGrid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(300px, 1fr))';
        galleryGrid.style.gap = '20px';
        
        // Justera bilderna i galleriet
        galleryItems.forEach(item => {
            item.style.position = 'relative';
            item.style.overflow = 'hidden';
            item.style.borderRadius = '10px';
            item.style.boxShadow = '0 4px 8px rgba(0,0,0,0.1)';
            item.style.transition = 'transform 0.3s ease';
            item.style.aspectRatio = '1';
            
            const img = item.querySelector('img');
            if (img) {
                img.style.width = '100%';
                img.style.height = '100%';
                img.style.objectFit = 'cover';
                img.style.display = 'block';
            }
            
            const caption = item.querySelector('.gallery-caption');
            if (caption) {
                caption.style.position = 'absolute';
                caption.style.bottom = '0';
                caption.style.left = '0';
                caption.style.right = '0';
                caption.style.background = 'rgba(0,0,0,0.7)';
                caption.style.color = 'white';
                caption.style.padding = '10px';
            }
        });
    }
}
                    

Bästa praxis för bildcollage

  1. Optimera bilderna för webben med rätt storlek och komprimering
  2. Var konsekvent med bildstorlekar och proportioner
  3. Använd CSS Grid eller Flexbox istället för tabeller eller flyt-baserade layouter
  4. Testa på olika enheter för att säkerställa responsivitet
  5. Använd lazy loading för bilder som inte är direkt synliga
  6. Inkludera alternativtext (alt) för alla bilder
  7. Överväg användning av thumbnails för stora bildsamlingar
Tips: När du felsöker bildcollage-problem, använd webbläsarens utvecklarverktyg för att inspektera CSS-stilar och kontrollera bildernas faktiska storlek och position.

Responsiv design

Responsiv design är avgörande för moderna webbplatser. Till skillnad från den gamla tekniken med frames, som var svår att göra responsiv, använder vår nya tekniklösning CSS och JavaScript för att anpassa sig efter användarens enhet.

Nyckelkomponenter i vår responsiva design

Mobil meny med toggle

På mobila enheter döljs sidopanelen som standard och kan visas med en menyknapp. Detta sparar värdefullt skärmutrymme på små enheter.

// Toggle för mobilmeny
document.querySelector('.menu-toggle')
    .addEventListener('click', function() {
    document.getElementById('sidebar')
        .classList.toggle('nav-active');
    document.querySelector('.overlay')
        .classList.toggle('overlay-active');
});

// Stäng menyn när man klickar utanför
document.querySelector('.overlay')
    .addEventListener('click', function() {
    document.getElementById('sidebar')
        .classList.remove('nav-active');
    document.querySelector('.overlay')
        .classList.remove('overlay-active');
});
                            

CSS Media Queries

Med media queries kan vi definiera olika stilar för olika skärmstorlekar. Detta är grundläggande för responsiv design.

/* Grundläggande stilar för alla skärmstorlekar */
#sidebar {
    width: 250px;
    position: fixed;
    height: 100vh;
    background-color: #f5f5f5;
    transition: transform 0.3s ease;
}

main {
    margin-left: 250px;
    padding: 20px;
    transition: margin-left 0.3s ease;
}

/* Anpassningar för små skärmar */
@media (max-width: 768px) {
    #sidebar {
        transform: translateX(-100%);
        z-index: 1000;
    }
    
    #sidebar.nav-active {
        transform: translateX(0);
    }
    
    main {
        margin-left: 0;
    }
    
    .menu-toggle {
        display: block;
    }
    
    .overlay {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: rgba(0,0,0,0.5);
        z-index: 900;
    }
    
    .overlay-active {
        display: block;
    }
}
                            

Responsiva bildcollage

För att göra bildcollagen responsiva använder vi CSS Grid med auto-fit och minmax:

.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 20px;
    padding: 20px;
}
                    

Denna CSS-kod gör att:

  • På stora skärmar visas flera bilder per rad
  • När skärmen blir mindre, minskar antalet bilder per rad automatiskt
  • På mobila enheter visas vanligtvis en bild per rad
  • Varje bild har minst 300px bredd, men kan expandera för att fylla tillgängligt utrymme

Viktiga principer för responsiv design

  1. Mobile First: Designa först för mobila enheter och lägg sedan till funktionalitet för större skärmar
  2. Flytande layout: Använd procentuella breddvärden istället för fasta pixelvärden
  3. Flexibla bilder: Säkerställ att bilder aldrig blir större än deras container
  4. Läsbar text: Se till att text alltid är läsbar oavsett skärmstorlek
  5. Touch-vänliga gränssnitt: Knappar och länkar bör vara tillräckligt stora för fingrar på pekskärmar
  6. Progressiv förbättring: Börja med grundläggande funktionalitet och lägg till mer avancerade funktioner för enheter som stöder dem

Testning av responsiv design

För att säkerställa att webbplatsen fungerar korrekt på alla enheter bör du regelbundet testa den på:

  • Olika webbläsare (Chrome, Firefox, Safari, Edge)
  • Olika skärmstorlekar (desktop, tablet, mobil)
  • Olika orientering (liggande och stående)
  • Olika pixeldensiteter (standardskärmar och högupplösta skärmar)
Vanliga fallgropar i responsiv design:
  • Att inte testa på tillräckligt många olika enheter
  • Att använda för små touch-mål på mobila enheter
  • Att hårdkoda pixelstorlekar istället för att använda relativa enheter
  • Att dölja viktigt innehåll på mobila enheter
  • Att inte optimera bilder för mobila nätverk

Sammanfattning och slutsatser

I denna dokumentation har vi utforskat de tekniska aspekterna av vår implementering av sidopanel och huvudinnehåll på kentlundgren.se. Här är de viktigaste punkterna att komma ihåg:

Arkitektur

  • Moderna HTML5 semantiska element ersätter föråldrade frames
  • CSS för layout och responsivitet på alla enheter
  • JavaScript och AJAX för dynamisk innehållsladdning
  • History API för korrekt URL-hantering och navigering

Nyckelkomponenter

  • Sidopanelen: Fast navigationspanel med länkar
  • Huvudinnehållet: Dynamiskt innehåll som uppdateras via AJAX
  • Bildcollage: Responsiva rutnät för bildvisning
  • Länkhantering: Intelligent hantering av relativa och absoluta länkar

Fördelar med den nya tekniklösningen

  • Bättre användarupplevelse med smidigare navigering
  • Förbättrad responsivitet på olika enheter
  • Bättre SEO genom semantisk HTML5
  • Enklare underhåll och uppdatering
  • Framtidssäkrad kodstruktur enligt moderna standarder

Framtida utvecklingsmöjligheter

  • Implementera en laddningsindikator för AJAX-anrop
  • Lägga till animationer vid innehållsbyten
  • Förbättra cachehantering för snabbare navigering
  • Implementera lazy loading för bilder i galleriet
  • Lägga till lightbox-funktionalitet för bildvisning
Slutsats: Genom att ersätta föråldrad frame-teknik med moderna webbstandarder har vi skapat en webbplats som är snabbare, mer tillgänglig och fungerar sömlöst på alla enheter. Den bevarar det bästa från den tidigare versionen (fast sidopanel och dynamiskt innehåll) samtidigt som den drar nytta av modern webbteknik.
Tillbaka till hemsidan