[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"\u002Fblog\u002Ftailwind-css-add-sticky-header-on-scroll":3,"$fpbVVJcVgNTsIAtOu18d8AAReoetH0ZZPOpsVVMR0fBk":727,"i-local-icon:calendar-days":731,"i-local-icon:tag":735,"i-local-icon:eye":737},{"id":4,"title":5,"body":6,"categories_slug":714,"comment_count":718,"date_published":719,"description":720,"extension":721,"meta":722,"navigation":117,"path":723,"seo":724,"stem":725,"__hash__":726},"content\u002Fblog\u002F21.tailwind-css-add-sticky-header-on-scroll.md","Tailwind CSS - Adding a Shadow to Sticky Headers on Scroll in Vue\u002FNuxt",{"type":7,"value":8,"toc":708},"minimark",[9,13,18,23,27,35,39,46,349,352,361,365,368,656,659,666,670,704],[10,11,5],"h1",{"id":12},"tailwind-css-adding-a-shadow-to-sticky-headers-on-scroll-in-vuenuxt",[14,15],"meta-card",{":categories":16,":date":17},"categories_slug","date_published",[19,20,22],"h2",{"id":21},"overview","Overview",[24,25,26],"p",{},"A sticky header is a standard UI pattern, but it often looks \"flat\" when it blends into the content scrolling underneath it. The best way to solve this is to apply a shadow (or a background or both), but only once the user has started scrolling.",[24,28,29,30,34],{},"Implementing a \"shadow on scroll\" effect in Vue or Nuxt is straightforward using the Composition API and Tailwind CSS 4. Instead of monitoring the scroll position directly (which can be heavy on performance), we can use an ",[31,32,33],"code",{},"IntersectionObserver"," to toggle a reactive state.",[19,36,38],{"id":37},"step-1-the-template-html-structure","Step 1: The Template (HTML) Structure",[24,40,41,42,45],{},"First, we need a \"sentinel\" element. This is an invisible ",[31,43,44],{},"div"," placed at the very top of the page. When this element leaves the viewport, we know the user has scrolled and the header has become \"stuck.\"",[47,48,53],"pre",{"className":49,"code":50,"language":51,"meta":52,"style":52},"language-html shiki shiki-themes github-dark","\u003Ctemplate>\n  \u003C!-- The Sentinel: Invisible element at the very top -->\n  \u003Cdiv ref=\"sentinel\" class=\"absolute top-0 h-1 w-full\">\u003C\u002Fdiv>\n\n  \u003Cheader\n    :class=\"[\n      'sticky top-0 z-50 bg-white transition-all duration-300',\n      isScrolled\n        ? 'border-b border-gray-200 bg-gray-100! shadow-md'\n        : 'border-b-transparent shadow-none',\n    ]\">\n    \u003Cnav class=\"mx-auto flex max-w-3xl items-center justify-between p-6\">\n      \u003Cspan class=\"font-bold\">Web Store\u003C\u002Fspan>\n      \u003Cul class=\"flex gap-4\">\n        \u003Cli>Features\u003C\u002Fli>\n        \u003Cli>Pricing\u003C\u002Fli>\n      \u003C\u002Ful>\n    \u003C\u002Fnav>\n  \u003C\u002Fheader>\n\n  \u003C!-- Spacer for content -->\n  \u003Cmain class=\"mx-auto min-h-200 max-w-3xl p-8\">\n    \u003Cp>Scroll down to see the header effect...\u003C\u002Fp>\n  \u003C\u002Fmain>\n\u003C\u002Ftemplate>\n","html","",[31,54,55,71,78,112,119,127,138,144,150,156,162,170,188,210,227,243,257,267,277,288,293,299,316,330,339],{"__ignoreMap":52},[56,57,60,64,68],"span",{"class":58,"line":59},"line",1,[56,61,63],{"class":62},"s95oV","\u003C",[56,65,67],{"class":66},"s4JwU","template",[56,69,70],{"class":62},">\n",[56,72,74],{"class":58,"line":73},2,[56,75,77],{"class":76},"sAwPA","  \u003C!-- The Sentinel: Invisible element at the very top -->\n",[56,79,81,84,86,90,93,97,100,102,105,108,110],{"class":58,"line":80},3,[56,82,83],{"class":62},"  \u003C",[56,85,44],{"class":66},[56,87,89],{"class":88},"svObZ"," ref",[56,91,92],{"class":62},"=",[56,94,96],{"class":95},"sU2Wk","\"sentinel\"",[56,98,99],{"class":88}," class",[56,101,92],{"class":62},[56,103,104],{"class":95},"\"absolute top-0 h-1 w-full\"",[56,106,107],{"class":62},">\u003C\u002F",[56,109,44],{"class":66},[56,111,70],{"class":62},[56,113,115],{"class":58,"line":114},4,[56,116,118],{"emptyLinePlaceholder":117},true,"\n",[56,120,122,124],{"class":58,"line":121},5,[56,123,83],{"class":62},[56,125,126],{"class":66},"header\n",[56,128,130,133,135],{"class":58,"line":129},6,[56,131,132],{"class":88},"    :class",[56,134,92],{"class":62},[56,136,137],{"class":95},"\"[\n",[56,139,141],{"class":58,"line":140},7,[56,142,143],{"class":95},"      'sticky top-0 z-50 bg-white transition-all duration-300',\n",[56,145,147],{"class":58,"line":146},8,[56,148,149],{"class":95},"      isScrolled\n",[56,151,153],{"class":58,"line":152},9,[56,154,155],{"class":95},"        ? 'border-b border-gray-200 bg-gray-100! shadow-md'\n",[56,157,159],{"class":58,"line":158},10,[56,160,161],{"class":95},"        : 'border-b-transparent shadow-none',\n",[56,163,165,168],{"class":58,"line":164},11,[56,166,167],{"class":95},"    ]\"",[56,169,70],{"class":62},[56,171,173,176,179,181,183,186],{"class":58,"line":172},12,[56,174,175],{"class":62},"    \u003C",[56,177,178],{"class":66},"nav",[56,180,99],{"class":88},[56,182,92],{"class":62},[56,184,185],{"class":95},"\"mx-auto flex max-w-3xl items-center justify-between p-6\"",[56,187,70],{"class":62},[56,189,191,194,196,198,200,203,206,208],{"class":58,"line":190},13,[56,192,193],{"class":62},"      \u003C",[56,195,56],{"class":66},[56,197,99],{"class":88},[56,199,92],{"class":62},[56,201,202],{"class":95},"\"font-bold\"",[56,204,205],{"class":62},">Web Store\u003C\u002F",[56,207,56],{"class":66},[56,209,70],{"class":62},[56,211,213,215,218,220,222,225],{"class":58,"line":212},14,[56,214,193],{"class":62},[56,216,217],{"class":66},"ul",[56,219,99],{"class":88},[56,221,92],{"class":62},[56,223,224],{"class":95},"\"flex gap-4\"",[56,226,70],{"class":62},[56,228,230,233,236,239,241],{"class":58,"line":229},15,[56,231,232],{"class":62},"        \u003C",[56,234,235],{"class":66},"li",[56,237,238],{"class":62},">Features\u003C\u002F",[56,240,235],{"class":66},[56,242,70],{"class":62},[56,244,246,248,250,253,255],{"class":58,"line":245},16,[56,247,232],{"class":62},[56,249,235],{"class":66},[56,251,252],{"class":62},">Pricing\u003C\u002F",[56,254,235],{"class":66},[56,256,70],{"class":62},[56,258,260,263,265],{"class":58,"line":259},17,[56,261,262],{"class":62},"      \u003C\u002F",[56,264,217],{"class":66},[56,266,70],{"class":62},[56,268,270,273,275],{"class":58,"line":269},18,[56,271,272],{"class":62},"    \u003C\u002F",[56,274,178],{"class":66},[56,276,70],{"class":62},[56,278,280,283,286],{"class":58,"line":279},19,[56,281,282],{"class":62},"  \u003C\u002F",[56,284,285],{"class":66},"header",[56,287,70],{"class":62},[56,289,291],{"class":58,"line":290},20,[56,292,118],{"emptyLinePlaceholder":117},[56,294,296],{"class":58,"line":295},21,[56,297,298],{"class":76},"  \u003C!-- Spacer for content -->\n",[56,300,302,304,307,309,311,314],{"class":58,"line":301},22,[56,303,83],{"class":62},[56,305,306],{"class":66},"main",[56,308,99],{"class":88},[56,310,92],{"class":62},[56,312,313],{"class":95},"\"mx-auto min-h-200 max-w-3xl p-8\"",[56,315,70],{"class":62},[56,317,319,321,323,326,328],{"class":58,"line":318},23,[56,320,175],{"class":62},[56,322,24],{"class":66},[56,324,325],{"class":62},">Scroll down to see the header effect...\u003C\u002F",[56,327,24],{"class":66},[56,329,70],{"class":62},[56,331,333,335,337],{"class":58,"line":332},24,[56,334,282],{"class":62},[56,336,306],{"class":66},[56,338,70],{"class":62},[56,340,342,345,347],{"class":58,"line":341},25,[56,343,344],{"class":62},"\u003C\u002F",[56,346,67],{"class":66},[56,348,70],{"class":62},[24,350,351],{},"Example showing what the header looks like before the user has scrolled.",[24,353,354],{},[355,356],"img",{"alt":357,"className":358,"src":360},"Tailwind Sticky Header Image 1",[359],"blog-image","\u002Fimages\u002Fblog\u002Ftailwind-sticky-header\u002Ftailwind-sticky-header-1.webp",[19,362,364],{"id":363},"step-2-vuenuxt-script-logic","Step 2: Vue\u002FNuxt Script Logic",[24,366,367],{},"A Vue JS example using composition API and Typescript.",[47,369,373],{"className":370,"code":371,"language":372,"meta":52,"style":52},"language-typescript shiki shiki-themes github-dark","\u003Cscript setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted } from \"vue\";\n\nconst isScrolled = ref(false);\nconst sentinel = ref(null);\n\nlet observer: IntersectionObserver | null = null;\n\nonMounted(() => {\n  observer = new IntersectionObserver(\n    ([entry]) => {\n      \u002F\u002F When the sentinel is out of view, we are \"scrolled\"\n      isScrolled.value = !entry?.isIntersecting;\n    },\n    {\n      threshold: [1.0],\n    },\n  );\n\n  if (sentinel.value) {\n    observer.observe(sentinel.value);\n  }\n});\n\nonUnmounted(() => {\n  if (observer) observer.disconnect();\n});\n\u003C\u002Fscript>\n","typescript",[31,374,375,390,407,411,434,452,456,482,486,500,515,531,536,549,554,559,570,574,579,583,591,602,607,612,616,627,641,646],{"__ignoreMap":52},[56,376,377,380,383,385,388],{"class":58,"line":59},[56,378,63],{"class":379},"snl16",[56,381,382],{"class":62},"script setup lang",[56,384,92],{"class":379},[56,386,387],{"class":95},"\"ts\"",[56,389,70],{"class":379},[56,391,392,395,398,401,404],{"class":58,"line":73},[56,393,394],{"class":379},"import",[56,396,397],{"class":62}," { ref, onMounted, onUnmounted } ",[56,399,400],{"class":379},"from",[56,402,403],{"class":95}," \"vue\"",[56,405,406],{"class":62},";\n",[56,408,409],{"class":58,"line":80},[56,410,118],{"emptyLinePlaceholder":117},[56,412,413,416,420,423,425,428,431],{"class":58,"line":114},[56,414,415],{"class":379},"const",[56,417,419],{"class":418},"sDLfK"," isScrolled",[56,421,422],{"class":379}," =",[56,424,89],{"class":88},[56,426,427],{"class":62},"(",[56,429,430],{"class":418},"false",[56,432,433],{"class":62},");\n",[56,435,436,438,441,443,445,447,450],{"class":58,"line":121},[56,437,415],{"class":379},[56,439,440],{"class":418}," sentinel",[56,442,422],{"class":379},[56,444,89],{"class":88},[56,446,427],{"class":62},[56,448,449],{"class":418},"null",[56,451,433],{"class":62},[56,453,454],{"class":58,"line":129},[56,455,118],{"emptyLinePlaceholder":117},[56,457,458,461,464,467,470,473,476,478,480],{"class":58,"line":140},[56,459,460],{"class":379},"let",[56,462,463],{"class":62}," observer",[56,465,466],{"class":379},":",[56,468,469],{"class":88}," IntersectionObserver",[56,471,472],{"class":379}," |",[56,474,475],{"class":418}," null",[56,477,422],{"class":379},[56,479,475],{"class":418},[56,481,406],{"class":62},[56,483,484],{"class":58,"line":146},[56,485,118],{"emptyLinePlaceholder":117},[56,487,488,491,494,497],{"class":58,"line":152},[56,489,490],{"class":88},"onMounted",[56,492,493],{"class":62},"(() ",[56,495,496],{"class":379},"=>",[56,498,499],{"class":62}," {\n",[56,501,502,505,507,510,512],{"class":58,"line":158},[56,503,504],{"class":62},"  observer ",[56,506,92],{"class":379},[56,508,509],{"class":379}," new",[56,511,469],{"class":88},[56,513,514],{"class":62},"(\n",[56,516,517,520,524,527,529],{"class":58,"line":164},[56,518,519],{"class":62},"    ([",[56,521,523],{"class":522},"s9osk","entry",[56,525,526],{"class":62},"]) ",[56,528,496],{"class":379},[56,530,499],{"class":62},[56,532,533],{"class":58,"line":172},[56,534,535],{"class":76},"      \u002F\u002F When the sentinel is out of view, we are \"scrolled\"\n",[56,537,538,541,543,546],{"class":58,"line":190},[56,539,540],{"class":62},"      isScrolled.value ",[56,542,92],{"class":379},[56,544,545],{"class":379}," !",[56,547,548],{"class":62},"entry?.isIntersecting;\n",[56,550,551],{"class":58,"line":212},[56,552,553],{"class":62},"    },\n",[56,555,556],{"class":58,"line":229},[56,557,558],{"class":62},"    {\n",[56,560,561,564,567],{"class":58,"line":245},[56,562,563],{"class":62},"      threshold: [",[56,565,566],{"class":418},"1.0",[56,568,569],{"class":62},"],\n",[56,571,572],{"class":58,"line":259},[56,573,553],{"class":62},[56,575,576],{"class":58,"line":269},[56,577,578],{"class":62},"  );\n",[56,580,581],{"class":58,"line":279},[56,582,118],{"emptyLinePlaceholder":117},[56,584,585,588],{"class":58,"line":290},[56,586,587],{"class":379},"  if",[56,589,590],{"class":62}," (sentinel.value) {\n",[56,592,593,596,599],{"class":58,"line":295},[56,594,595],{"class":62},"    observer.",[56,597,598],{"class":88},"observe",[56,600,601],{"class":62},"(sentinel.value);\n",[56,603,604],{"class":58,"line":301},[56,605,606],{"class":62},"  }\n",[56,608,609],{"class":58,"line":318},[56,610,611],{"class":62},"});\n",[56,613,614],{"class":58,"line":332},[56,615,118],{"emptyLinePlaceholder":117},[56,617,618,621,623,625],{"class":58,"line":341},[56,619,620],{"class":88},"onUnmounted",[56,622,493],{"class":62},[56,624,496],{"class":379},[56,626,499],{"class":62},[56,628,630,632,635,638],{"class":58,"line":629},26,[56,631,587],{"class":379},[56,633,634],{"class":62}," (observer) observer.",[56,636,637],{"class":88},"disconnect",[56,639,640],{"class":62},"();\n",[56,642,644],{"class":58,"line":643},27,[56,645,611],{"class":62},[56,647,649,651,654],{"class":58,"line":648},28,[56,650,344],{"class":379},[56,652,653],{"class":62},"script",[56,655,70],{"class":379},[24,657,658],{},"Example showing what the header looks after the user has scrolled.",[24,660,661],{},[355,662],{"alt":663,"className":664,"src":665},"Tailwind Sticky Header Image 2",[359],"\u002Fimages\u002Fblog\u002Ftailwind-sticky-header\u002Ftailwind-sticky-header-2.webp",[19,667,669],{"id":668},"notes-on-tailwind-classes","Notes on Tailwind Classes",[217,671,672,678,684,690],{},[235,673,674,677],{},[31,675,676],{},"sticky top-0",": Keeps the header at the top of the page.",[235,679,680,683],{},[31,681,682],{},"transition-all duration-300",": In Tailwind 4, this ensures the shadow and background fades in and out smoothly rather than snapping instantly.",[235,685,686,689],{},[31,687,688],{},"z-50",": In Tailwind 4, this is the z-index. Ensures that header section sits above content being scrolled below.",[235,691,692,695,696,699,700,703],{},[31,693,694],{},"bg-gray-100!",": In Tailwind 4, ",[31,697,698],{},"!"," is the equivalent to CSS ",[31,701,702],{},"!important",". Ensures that the background color specified here takes precedence.",[705,706,707],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":52,"searchDepth":73,"depth":73,"links":709},[710,711,712,713],{"id":21,"depth":73,"text":22},{"id":37,"depth":73,"text":38},{"id":363,"depth":73,"text":364},{"id":668,"depth":73,"text":669},[715,716,717],"tailwind","nuxt","vue",null,"2025-12-30","A sticky header is a standard UI pattern, but it often looks 'flat' when it blends into the content scrolling underneath it. The best way to solve this is to apply a shadow\u002Fbackground, but only once the user has started scrolling.","md",{},"\u002Fblog\u002Ftailwind-css-add-sticky-header-on-scroll",{"title":5,"description":720},"blog\u002F21.tailwind-css-add-sticky-header-on-scroll","SyHPXWEaztXkmrwUQ_wVfxvjpXeiHBcZDwpUNYUPiAA",[728],{"id":729,"name":723,"count":730},38,735,{"left":732,"top":732,"width":332,"height":332,"rotate":732,"vFlip":733,"hFlip":733,"body":734},0,false,"\u003Cpath\n    fill=\"none\"\n    stroke=\"currentColor\"\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n    stroke-width=\"1.5\"\n    d=\"M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5m-9-6h.008v.008H12zM12 15h.008v.008H12zm0 2.25h.008v.008H12zM9.75 15h.008v.008H9.75zm0 2.25h.008v.008H9.75zM7.5 15h.008v.008H7.5zm0 2.25h.008v.008H7.5zm6.75-4.5h.008v.008h-.008zm0 2.25h.008v.008h-.008zm0 2.25h.008v.008h-.008zm2.25-4.5h.008v.008H16.5zm0 2.25h.008v.008H16.5z\" \u002F>",{"left":732,"top":732,"width":332,"height":332,"rotate":732,"vFlip":733,"hFlip":733,"body":736},"\u003Cg\n    fill=\"none\"\n    stroke=\"currentColor\"\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n    stroke-width=\"1.5\">\n    \u003Cpath\n      d=\"M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.1 18.1 0 0 0 5.224-5.223c.54-.827.368-1.908-.33-2.607l-9.583-9.58A2.25 2.25 0 0 0 9.568 3\" \u002F>\n    \u003Cpath d=\"M6 6h.008v.008H6z\" \u002F>\n  \u003C\u002Fg>",{"left":732,"top":732,"width":332,"height":332,"rotate":732,"vFlip":733,"hFlip":733,"body":738},"\u003Cg\n    fill=\"none\"\n    stroke=\"currentColor\"\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n    stroke-width=\"1.5\">\n    \u003Cpath\n      d=\"M2.036 12.322a1 1 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178c.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178\" \u002F>\n    \u003Cpath d=\"M15 12a3 3 0 1 1-6 0a3 3 0 0 1 6 0\" \u002F>\n  \u003C\u002Fg>"]