Jeg startede denne blog i 2009 og den første version var bygget helt fra bunden hvor jeg havde kodet det hele i PHP med MySQL som database.

I 2013 flyttede jeg bloggen over på WordPress hvor alt HTML blev konverteret til WordPress templates, men designet forblev nogenlunde uændret.

I 2014 lavede jeg designet responsivt så siden blev mobilvenlig.

I 2019 var det så tid til et redesign hvor jeg har brugt data fra Google Analytics, både til at tage beslutninger for at få sitet til at loade så hurtigt som muligt, men også til at forbedre KPI’erne for sitet. Jeg har også testet et råd fra Steve Krug’s Dont Make Me Think, for at få brugerne til at læse flere af mine blogindlæg.

Jeg har delt blogindlægget op i to dele, hvor den første del fokuserer på hvordan jeg har kodet sitet (der er brugt GA data to steder, som er markeret med fed herunder).

Den anden del fokuserer på optimering med Google Analytics og opfølgning på effekten af de ændringer jeg har lavet.

Indhold

Part 1: Det tekniske med fokus på hvordan designet er kodet.

Part 2: Optimering af adfærden på sitet med Google Analytics data.

Mål med det nye design

  1. Mere moderne workflow til udvikling af websitet
  2. Oprydning i kode og sletning af unødvendige ting
  3. Hurtigere loadtid
  4. Højere konvertering
  5. Øge sidevisninger pr. besøg

Workflow

GruntJS

Der er mange (kedelige) opgaver involveret i at optimere front-end kode og jeg bruger GruntJS som task runner, til at udføre alle de opgaver automatisk.

GruntJS gør følgende ved mine filer:

  1. Samler JavaScript filer til én samlet, minificeret fil. Både de forskellige libraries jeg bruger og mine egne JavaScript filer.
  2. Compiler alle Sass filerne til en minificeret CSS fil.
  3. Kopiere alle de færdige optimerede filer over i en mappe, som indeholder de filer der skal uploades til webserveren.
  4. JavaScript filen bliver cachet 1 år i browseren og derfor får den et unikt nyt navn, hvis filen ændres, så browseren downloader den nye fil.
  5. Grunt overvåger mine filer og kører de ovenstående opgaver, når jeg gemmer en ny ændring, fx i en Sass eller JavaScript fil.

Kun én JavaScript fil

Det er ikke så vigtigt efter HTTP2 blev lanceret, men det er stadig best practise at lave så få requests som muligt, fx ved at samle alt JavaScript i én fil. Takket være GruntJS bliver dette gjort helt automatisk og derefter bliver filen minificeret.

Browser caching og cache busting

JS filen caches i browseren i 1 år ved at sætte expire-headers til 1 år i .htaccess. Jeg har brugt de anbefalede settings fra html5boilerplate.

ExpiresByType application/javascript                "access plus 1 year"
ExpiresByType application/x-javascript              "access plus 1 year"
ExpiresByType text/javascript                       "access plus 1 year"

GruntJS laver et hash baseret på indholdet i filen og det hash bliver tilføjet til filnavnet.

Dvs. denne fil:

bundle.min.js

Kommer til at hedde:

bundle.min.e3d609e4.js

Hvis filen ændrer sig bliver der lavet en ny hash, så filnavnet ændrer sig. Dermed vil browseren se det som en ny fil, så den ikke bruger den fil den allerede har i cache, men downloade den nye fil.

Dermed kan jeg have filen cachet nærmest uendeligt, men stadig sikre at alle browsere får den nyeste fil, hvis jeg ændrer noget.

Gruntfile.js

For de interesserede, så er her min Gruntfile.js som indeholder hele den opsætning af GruntJS som er beskrevet herover. Derudover har den også compile af Sass til CSS som jeg beskriver om lidt.

module.exports = function(grunt) {
    require("load-grunt-tasks")(grunt);

    // 1. All configuration goes here
    grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),

        // grunt-contrib-concat
        concat: {
            dist: {
                src: [
                    "js/libs/prism.js",
                    "js/SlideUpBox.js",
                    "js/content-as-ecommerce.js",
                    "js/tracking.js",
                    "js/hamburgerNav.js",
                    "js/jacobworsoe.js",
                    "js/drinksberegner.js"
                ],
                dest: "js/build/bundle.js"
            }
        },

        // grunt-contrib-uglify
        uglify: {
            build: {
                files: [
                    {
                        src: "js/build/bundle.js",
                        dest: "js/build/bundle.min.js"
                    }
                ]
            }
        },

        // grunt-contrib-sass
        sass: {
            dist: {
                options: {
                    style: "compressed",
                    sourcemap: "none"
                },
                files: {
                    "css/homepage.css": "scss/homepage-bundle.sass",
                    "css/single.css": "scss/single-bundle.sass"
                }
            }
        },

        // grunt-contrib-copy
        copy: {
            main: {
                files: [
                    {
                        expand: true,
                        src: ["*.php"],
                        dest: "dist/",
                        filter: "isFile"
                    },
                    {
                        expand: true,
                        src: ["*.css"],
                        dest: "dist/",
                        filter: "isFile"
                    },
                    {
                        expand: true,
                        src: ["*.png"],
                        dest: "dist/",
                        filter: "isFile"
                    },
                    {
                        expand: true,
                        src: ["css/*.css"],
                        dest: "dist/",
                        filter: "isFile"
                    },
                    {
                        expand: true,
                        src: ["js/build/*.min.js"],
                        dest: "dist/",
                        filter: "isFile"
                    },
                    {
                        expand: true,
                        src: ["svg/*.svg"],
                        dest: "dist/",
                        filter: "isFile"
                    }
                ]
            }
        },

        // grunt-hashres
        hashres: {
            options: {
                fileNameFormat: "${name}.${hash}.${ext}",
                renameFiles: true
            },
            prod: {
                options: {},
                src: ["dist/js/**/*.min.js"],
                dest: ["dist/footer.php"]
            }
        },

        // grunt-contrib-watch
        watch: {
            options: {
                livereload: true
            },
            scripts: {
                files: ["js/*.js"],
                tasks: ["concat", "uglify"],
                options: {
                    spawn: false
                }
            },
            css: {
                files: ["scss/*.sass", "scss/*.scss"],
                tasks: ["sass"],
                options: {
                    spawn: false
                }
            }
        }

    }); // grunt.initConfig

    // 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
    // Development tasks
    grunt.registerTask("default", ["sass", "watch"]);
    grunt.registerTask("stage", ["sass", "concat", "uglify", "copy"]);
    grunt.registerTask("deploy", [
        "sass",
        "concat",
        "uglify",
        "copy",
        "hashres"        
    ]);
};

JavaScript

Tracking logik fra GTM til dataLayer

Jeg afprøver og tester en masse forskellig tracking på mit website. Noget af det er tilføjet til sitets JavaScript fil og udstillet i dataLayer som det bør, men noget bliver også hurtigt tilføjet direkte i GTM for at afprøve det.

Jeg gik alt GTM kode igennem og fik det flyttet til sitet, så GTM indeholder så lidt kode og logik som muligt. Det er fint at teste noget hurtigt i GTM, men det skal tilføjes til dataLayer hvis det skal være permanent.

Væk med jQuery

Jeg har omskrevet alt jQuery til ren JavaScript for at slippe for at loade de 87 KB som jQuery fylder når det er minified (274 KB unminified). Her var youmightnotneedjquery.com en stor hjælp.

JavaScript reduceret fra 184 KB til 31 KB

JavaScript koden til websitet er reduceret kraftigt med i alt 153 KB hvoraf de 87 KB er jQuery. Men der er også en masse andre ting jeg har skåret væk og skrevet smartere. Fx FitVids.JS som jeg brugte da jeg lavede sitet responsivt til at gøre YouTube videoer responsive. Det er meget smart, men med lidt simpel HTML og CSS kan man undvære det jQuery plugin.

Jeg indsætter en div rundt om videoen.

<div class="videoWrapper">
    <iframe src="//www.youtube.com/embed/usyYXNNBRjc" frameborder="0" allowfullscreen></iframe>
</div>

Og tilføjer lidt styling af den div samt iframen som indeholder videoen, og så er videoen responsiv.

.videoWrapper
    position: relative
    padding-bottom: 56.25%
    padding-top: 25px
    height: 0
    margin: 20px 0 20px 0
    border: 5px solid $lightGrey

.videoWrapper iframe
    position: absolute
    top: 0
    left: 0
    width: 100%
    height: 100%

Jeg brugte også LunaMetrics’ script til tracking af visninger af YouTube videoer, men jeg brugte ikke den tracking til noget, så det blev også fjernet.

CSS

CSS skrevet i Sass

I det nye redesign skrev jeg alt CSS fra bunden igen og jeg valgte at skrive det i Sass.

Sass er et sprog som giver nogle ekstra muligheder og Sass filerne skal compiles til almindelige CSS filer inden de ryger ud på websitet.

Her er mine top 3 fedeste ting ved Sass.

1) Variabler. Jeg kan definere variabler, fx til farvekoder som er brugt mange steder i koden. Dermed kan du nemt skifte farven overalt i din kode, blot ved at ændre én variabel. Da facebook valgte at rydde op i deres CSS, fandt de 800 næsten ens blå farver i koden. Det sker ikke med Sass.

Websitets farver defineret i Sass variabler.

Websitets farver defineret i Sass variabler.

2) Imports. Jeg kan splitte Sass koden op i mindre filer som tilhører en bestemt side eller sektion af sitet. Det hele kan samles til én fil, så browseren stadig kun skal lave et request.

// Base
@import "normalize"
@import "_vars"
@import "_base"
@import "_jetpack"

// Critical
@import "_header"
@import "_videoEmbeds"
@import "_pre"
@import "_button"
@import "_blockquote"

// Below-the-fold
@import "_post-share-follow"
@import "_comments"
@import "_footer"
@import "_slide-up-box"

// Pages
@import "_single"
@import "_highlight"
@import "_tables"

3) Nesting. Med Nesting kan man tilføje underliggende selectors blot ved at indent’e linjen, så man slipper for at gentage selectors mange gange.

Her er et eksempel på Nesting i Sass.

.comment-gravatar
    float: left
    width: 15%
    max-width: 120px
    padding-right: 20px
    margin-top: 10px

    @media(max-width: 500px)
        padding-right: 8px

    img
        border-radius: 5px

Og her den CSS kode det compiles til.

.comment-gravatar {
  float: left;
  width: 15%;
  max-width: 120px;
  padding-right: 20px;
  margin-top: 10px;
}

@media(max-width: 500px) {
  .comment-gravatar {
    padding-right: 8px;
  }
}

.comment-gravatar img {
  border-radius: 5px;
}

Inline CSS eller ekstern fil?

Normalt er det best practice at have CSS i en ekstern fil, så den kan caches i browseren. Men det kræver et ekstra request at have den i en ekstern fil. Så om det kan betale sig at lave et ekstra request kommer an på hvor stor filen er samt hvor mange sider brugeren ser på sitet.

På første sidevisning vil det nemlig være en ulempe at have CSS i en ekstern fil, da der skal laves et request mere. Men på efterfølgende sider vil filen være cachet og skal ikke hentes igen.

Bounce rate på 85% og 1,17 sider pr. session

Bounce rate på 85% og 1,17 sider pr. session

I løbet af det sidste år har der været en bounce rate på 85% på sitet, dvs. langt de fleste læser kun et enkelt blogindlæg. Der bliver også kun set 1,17 sider pr. session. Det betyder altså at 85% af de besøgende ikke ser en efterfølgende side og dermed ikke får gevinsten af en cachet CSS fil.

I hvert fald ikke i det samme besøg. Men det kan jo være de kommer tilbage på sitet igen og dermed stadig har CSS filen i deres cache.

25% af de besøgende har været på sitet før.

25% af de besøgende har været på sitet før.

Det er kun 25% af de besøgende der har været på sitet før, så langt de fleste vil ikke have CSS filen cachet.

Bemærk! Jeg har ekskluderet Safari her, da Safari ikke længere giver korrekte tal for tilbagevendende besøg efter ITP 2.1.

Min konklusion på de ovenstående data bliver at det er bedst at optimere efter at give en hurtig oplevelse på den første sidevisning og derfor lægger jeg CSS’en inline, for at spare det ekstra request.

Inline kun den nødvendige CSS kode

Når man har CSS i en ekstern fil som bliver cachet giver det typisk bedst mening at samle det hele i én fil. Men når jeg inliner min CSS kode, giver det bedre mening kun at inline den CSS kode der skal bruges på den specifikke side.

Min forside er rimelig simpel. I mit tilfælde er det bare en liste af mine blogindlæg med titel, dato og antal kommentarer.

Forsiden er meget simpel.

Forsiden er meget simpel.

Et blogindlæg har både billeder og video i indlægget, den har en anderledes header med titlen på indlægget. I bunden er der links til sociale medier, tilmelding til nyhedsbrev og så er der hele kommentar sektionen, som kræver en masse CSS kode.

Et blogindlæg kræver noget mere CSS.

Et blogindlæg kræver noget mere CSS.

Der er altså en masse CSS kode som er helt overflødig at loade på forsiden og vice versa.

Når jeg bruger Sass til at samle de enkelte .sass filer til en færdig CSS fil laver jeg derfor to filer:

  1. En til forsiden og kategorisider, hvor der blot vises en liste af indlæg.
  2. En til single.php som viser hele indlægget.

De to filer har alt den generelle styling til fælles, som jeg har brudt ud i logiske moduler.

// Base
@import "normalize" // https://necolas.github.io/normalize.css/
@import "_vars" // Sass variabler med alle de farver jeg bruger
@import "_base" // Site-wide styling, fx box-sizing: border-box og H1, H2, H3 og overordnet font-family
@import "_jetpack" // Jetpack tilføjer en lille statistik box, som jeg sjuler med CSS

// Critical - Above-the-fold
@import "_header" // SVG logo, sidens titel, hamburger menuen og selve menuen som åbnes

// Below-the-fold
@import "_footer" // Footer med sociale links, mit billede og en række links

Ovenstående CSS kode inkluderes i begge filer og derudover inkluderer jeg så den kode som er relevant for hhv. forsiden og blogindlægget.

Jeg har i alt 19 KB CSS kode.

  • 41% er Normalize, som jeg måske skal overveje om jeg kan undvære.
  • 38% er specifikt til blogindlæg som jeg derved ikke behøver at loade på forsiden.
  • 18% er den globale CSS til header og footer.
  • 3% er til forsiden, der som sagt er meget simpel.

I WordPress inkluderer jeg de to CSS filer så de ligger inline, baseret på et check for om siden er single.php eller andre sider.

<style>
<?php if ( is_single() || is_page() ) {
    include("css/single.css");
} else {
    include("css/homepage.css");
}
?>
</style>

CSS reduceret med 45%

CSS kode har det med at vokse over tid og man får sjældent ryddet op løbende. Her skrev jeg alt fra bunden og jeg er nok også blevet bedre til at skrive CSS så det fylder mindre. Resultatet er en 45% reduktion af CSS fra 34,5 KB på i det gamle design til 19 KB i det nye design.

Væk med !important

Når jeg skriver !important i min CSS kode er det et tegn på at jeg har malet mig op i et hjørne.

Det er en sidste udvej. Og det kommer til at bide mig i røven senere hen.

Det gamle design brugte !important 35 gange.

Derfor har jeg fokuseret på at få gennemtænkt mine CSS selectors så jeg undgår at bruge !important i det nye design.

Jeg har også tænkt over at min styling skal cascade så meget som muligt, så jeg definerer mest muligt CSS kode på de øverste selectors (dem med lavest specificity) og derefter nedarves de bare til alt det øvrige. Det betyder også at jeg ikke overskriver min egen kode eller laver den samme styling flere gange på forskellige selectors.

Et eksempel er at jeg styler min font på html elementet og derefter nedarves det bare til resten af sitet, så jeg ikke skal style min font igen – lige bortset fra input elementer som ikke nedarver styling og derfor skal styling skrives specifikt på dem.

html
    background: $grey-dark
    color: $lightGrey
    font-family: -system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"
    -webkit-font-smoothing: antialiased
    -moz-osx-font-smoothing: grayscale
    letter-spacing: 0.02em
    font-size: 20px
    line-height: 1.75

Tit bruges !important også for at overskrive noget andet styling, fx noget CSS der kommer med et plugin. Det betyder dermed overflødig kode, som blot overskrives.

Det giver også browseren mere arbejde med at finde ud af hvad der skal overskrive noget andet.

Jeg har, så vidt det er muligt, deaktiveret den medfølgende CSS fra de enkelte plugins, så jeg blot får den rå HTML og selv skrevet alt styling. Dermed er jeg sikker på at der ikke kommer noget overflødig CSS kode med.

SVG til grafik

SVG er super fedt til grafiske elementer fordi det er kode og ikke et billede. Dermed kan det skalere uendeligt uden at blive grimt og det fylder meget lidt.

Jeg bruger det fx til den lille graf i mit logo.

Der er forskellige måder at indsætte et SVG billede på og jeg lytter til Chris Coyiers enorme erfaring om SVG (han har skrevet en bog om det: Practical SVG). Hans anbefaling er blot at inline SVG koden direkte i HTML’en. Det har han skrevet om her: A Pretty Good SVG Icon System

Jeg har alle mine SVG filer liggende i koden og selve koden til grafen i logoet kan ses herunder. Den fylder kun 848 bytes som SVG fil.

SVG koden til grafen som fylder under 1 KB

SVG koden til grafen som fylder under 1 KB

Indholdet af SVG filen indsætter jeg i header.php med følgende kode. Når SVG filen ligger i koden, skal der ikke laves et ekstra request for at hente den og færre requests er med til at gøre sitet hurtigt.

<a href="https://www.jacobworsoe.dk/" title="jacobworsoe.dk" rel="home" class="blog-title">
   <span class="logo"><?php include("svg/logo.svg"); ?></span>
   <span class="title"><?php bloginfo( 'name' ); ?></span>
</a>

WordPress

Væk med unødvendige plugins

Jeg har fået fjernet en del plugins, så det bliver mere simpelt, fjerner mulige sikkerhedshuller og gør sitet hurtigere.

Tabeller

TablePress er et fedt plugin, men de få simple tabeller jeg har i mine indlæg, kan jeg sagtens skrive i hånden, så væk med det.

Syntax highlighting

Jeg brugte Code Prettify til syntax highlighting af kode. Jeg er skiftet til at bruge Prism.js hvor jeg vælger præcis de kodesprog jeg skal bruge og så får jeg en CSS fil og en JavaScript fil. CSS filen inkluderer jeg i min SCSS fil og JavaScript filen bliver bundlet sammen med min øvrige JS kode i én samlet fil. Og så er dét plugin overflødigt :)

Jeg kan i øvrigt anbefale denne artikel med alle de forskellige muligheder for at skrive og vise kode i WordPress.

Relaterede indlæg

Jeg har brugt Yet Another Related Posts Plugin til at vise relaterede indlæg i bunden af hvert blogindlæg.

I bunden af alle blogindlæg vises links til relaterede blogindlæg.

I bunden af alle blogindlæg vises links til relaterede blogindlæg.

Jeg brugte Enhanced Ecommerce til at tracke impressions og clicks på dem og fandt ud af at de links havde en CTR på 0,42% så for 99,58% var de bare ligegyldigt støj på siden. Så jeg fjernede dem inklusiv det plugin.

CTR på 0,42% viser at meget få klikker på de links.

CTR på 0,42% viser at meget få klikker på de links.

Jeg fjernede i øvrigt også links til de seneste blogindlæg, da de havde en endnu lavere CTR på 0,05% – disse blev ikke lavet med et plugin, men det er altid godt at få fjernet unødvendigt støj.

Sidebar med nyeste blogindlæg.

Sidebar med nyeste blogindlæg.

Loadtid og konvertering

Okay, det var en lang teknisk snak. Nu skal vi se om det har givet de ønskede resultater.

Google Pagespeed Score hævet fra 86 til 99

Det gamle design havde en pagespeed score på 86 for desktop. Den er nu 99.

Pagespeed score på 99 for desktop.

Pagespeed score på 99 for desktop.

Den vigtige metric er dog mobile nu og den er 96.

Pagespeed score på 96 for mobile.

Pagespeed score på 96 for mobile.

Er sitet så blevet hurtigere?

Ja, det er det. I gennemsnit er loadtiden blevet forbedret 29%.

Den gennemsnitlige loadtid er forbedret 29%.

Den gennemsnitlige loadtid er forbedret 29%.

Men gennemsnit kan snyde meget og skjule sandheden.

Ikke alle sider loader lige hurtigt.

Jeg har blogindlæg på mere end 6000 ord med masser af billeder. Jeg har meget populære blogindlæg som står for 70% af sidevisningerne som kun har få, men til gengæld meget store billeder. Og så er der forsiden som stort set kun er tekst.

Især det faktum at de har meget forskellige antal sidevisninger gør at de fylder meget forskelligt i gennemsnittet.

Lad os derfor kigge på top 10 mest populære sider hver for sig, samt et vægtet gennemsnit for de sider. Alle top 10 sider er blevet hurtigere, men der er stor forskel på hvor meget de er forbedret.

Top 10 sider er i gennemsnit blevet 22% hurtigere - men der er store forskelle!

Top 10 sider er i gennemsnit blevet 22% hurtigere – men der er store forskelle!

22% er et mere retvisende gennemsnit for udviklingen i loadtid.

Hvad med konvertering?

Jeg har tidligere skrevet om hvordan jeg bruger Enhanced Ecommerce til at tracke om brugerne læser mine blogindlæg.

Kort fortalt tracker jeg hvor mange der scroller helt til bunden af et blogindlæg og har været mindst 1 minut på siden. Det er den vigtigste KPI for min blog. Hvor mange læser hele blogindlægget?

For hvert blogindlæg har jeg en Buy-to-detail Rate, som er forholdet mellem antal sidevisninger og antal læsninger.

På trods af at loadtiden er markant forbedret for alle blogindlæg, så er konverteringen desværre ikke steget – tværtimod.

Jeg tror den store årsag til den lavere konvertering skyldes designet. Jeg har skruet op for font-size fra 17px til 20px i det nye design og gjort overskrifter markant større og givet det hele lidt mere “luft”. Det gør det nemmere at læse, men siden bliver også markant længere. Måske føles det som et længere blogindlæg at tygge sig igennem?

Udvikling i konvertering - top 10 sider

Udvikling i konvertering – top 10 sider

Bounce Rate er ligeledes uændret, på trods af den hurtigere loadtid.

Bounce Rate før/efter designet.

Bounce Rate før/efter designet.

Så det nye design har ikke haft den ønskede effekt på konverteringen. Det må jeg gøre bedre i næste design.

Øge sidevisninger pr. besøg

Jeg har læst Don’t Make Me Think mange gange og den kan anbefales til alle der arbejder med noget digitalt.

Don't Make Me Think af Steve Krug

Don’t Make Me Think af Steve Krug

Her er en god pointe fra bogen omkring navigation.

Navigation reveals content!

Navigation reveals content!

Som tidligere vist, så har sitet en Bounce Rate på 85% og der bliver kun set 1,17 sider pr. session.

Bounce rate på 85% og 1,17 sider pr. session

Bounce rate på 85% og 1,17 sider pr. session

Jeg vil gerne at brugerne fortsætter rundt på sitet og ser nogle flere blogindlæg.

Det gamle design havde ikke en menu, så jeg tilføjede en burger menu som viser sitets kategorier, som Steve Krug anbefaler i Don’t Make Me Think.

Burger menu med kategorier.

Burger menu med kategorier.

Jeg tracker både hvor mange der åbner burger menuen og hvor mange der klikker i den.

  • 1,6% af alle besøg åbner menuen.
  • 0,4% af alle besøg klikker på noget i menuen.

Her er de kategorier der bliver klikket på.

Mest klikkede kategorier i burger menuen.

Mest klikkede kategorier i burger menuen.

Umiddelbart en ret lav konverteringsrate og sider pr. besøg er dog også uændret.

Pages per session er uændret.

Pages per session er uændret.

Så selvom Steve Krug har ret i mange ting, så virker en burger menu altså ikke på dette site. Jeg må i tænkeboks.

Til sammenligning er der 0,47% der klikker på et internt link når jeg i et blogindlæg, linker til et andet af mine blogindlæg.

Lykkedes målene?

Lad os se.

  1. Mere moderne workflow til udvikling af websitet – Tjek!
  2. Oprydning i kode og sletning af unødvendige ting – Tjek!
  3. Hurtigere loadtid – Tjek!
  4. Højere konvertering – Nope!
  5. Øge sidevisninger pr. besøg – Nope!