[{"data":1,"prerenderedAt":516},["ShallowReactive",2],{"en-main-blog_en-\u002Fblog\u002Flint-tailwind-css-v4-with-eslint\u002F":3,"en-alternative-blog_es-\u002Fes\u002Fblog\u002Flint-tailwind-css-v4-with-eslint":296,"blog_en-related-\u002Fblog\u002Flint-tailwind-css-v4-with-eslint\u002F":463},{"id":4,"title":5,"body":6,"date":280,"description":281,"extension":282,"image":283,"meta":284,"navigation":126,"path":285,"readingTime":286,"schemaOrg":283,"seo":287,"stem":288,"tags":289,"toc":294,"updatedAt":283,"__hash__":295},"blog_en\u002Fblog\u002F0006.lint-tailwind-css-v4-with-eslint.md","@angelblanco\u002Feslint-config 3.0 is out, now with Tailwind CSS",{"type":7,"value":8,"toc":276},"minimark",[9,26,46,49,54,57,86,93,211,228,232,241,260,267,272],[10,11,12,19,20,25],"p",{},[13,14,18],"a",{"href":15,"rel":16},"https:\u002F\u002Fgithub.com\u002Fangelblanco\u002Feslint-config",[17],"nofollow","@angelblanco\u002Feslint-config 3.0"," is available on npm. You can now lint your ",[13,21,24],{"href":22,"rel":23},"https:\u002F\u002Ftailwindcss.com\u002F",[17],"Tailwind CSS"," classes.",[10,27,28,29,33,34,39,40,45],{},"When I started this blog and ",[13,30,32],{"href":31},"\u002Fblog\u002Fcreate-your-eslint-config\u002F","created my own ESLint config"," in 2025, there was no ",[13,35,38],{"href":36,"rel":37},"https:\u002F\u002Feslint.org\u002F",[17],"ESLint"," plugin for Tailwind CSS v4 yet. In November 2026 I'm updating my open source repositories, and ",[13,41,44],{"href":42,"rel":43},"https:\u002F\u002Fgithub.com\u002Ffrancoismassart\u002Feslint-plugin-tailwindcss",[17],"eslint-plugin-tailwindcss"," is finally ready.",[10,47,48],{},"It's impressive how much the stack you use can change in a year, but I'll write about that in upcoming posts.",[50,51,53],"h2",{"id":52},"enable-tailwind-css-linting","Enable Tailwind CSS linting",[10,55,56],{},"Install the plugin (it requires Tailwind CSS v4):",[58,59,64],"pre",{"className":60,"code":61,"language":62,"meta":63,"style":63},"language-bash shiki shiki-themes catppuccin-latte catppuccin-mocha","pnpm add -D eslint-plugin-tailwindcss\n","bash","",[65,66,67],"code",{"__ignoreMap":63},[68,69,72,76,80,83],"span",{"class":70,"line":71},"line",1,[68,73,75],{"class":74},"sMmZK","pnpm",[68,77,79],{"class":78},"sgueU"," add",[68,81,82],{"class":78}," -D",[68,84,85],{"class":78}," eslint-plugin-tailwindcss\n",[10,87,88,89,92],{},"Then enable the ",[65,90,91],{},"tailwind"," option and point it to your CSS entry file:",[58,94,99],{"className":95,"code":96,"filename":97,"language":98,"meta":63,"style":63},"language-js shiki shiki-themes catppuccin-latte catppuccin-mocha","import eslintConfig from '@angelblanco\u002Feslint-config';\n\nexport default eslintConfig(defaultOptions => ({\n  ...defaultOptions,\n  tailwind: {\n    cssConfigPath: 'app\u002Fassets\u002Fcss\u002Fmain.css',\n  },\n}));\n","eslint.config.js","js",[65,100,101,121,128,156,169,181,194,200],{"__ignoreMap":63},[68,102,103,107,111,114,117],{"class":70,"line":71},[68,104,106],{"class":105},"sAS4m","import",[68,108,110],{"class":109},"s0aCy"," eslintConfig ",[68,112,113],{"class":105},"from",[68,115,116],{"class":78}," '@angelblanco\u002Feslint-config'",[68,118,120],{"class":119},"sxyLT",";\n",[68,122,124],{"class":70,"line":123},2,[68,125,127],{"emptyLinePlaceholder":126},true,"\n",[68,129,131,134,137,140,143,147,150,153],{"class":70,"line":130},3,[68,132,133],{"class":105},"export",[68,135,136],{"class":105}," default",[68,138,139],{"class":74}," eslintConfig",[68,141,142],{"class":109},"(",[68,144,146],{"class":145},"s2f33","defaultOptions",[68,148,149],{"class":105}," =>",[68,151,152],{"class":109}," (",[68,154,155],{"class":119},"{\n",[68,157,159,163,166],{"class":70,"line":158},4,[68,160,162],{"class":161},"sjJjI","  ...",[68,164,146],{"class":165},"seXix",[68,167,168],{"class":119},",\n",[68,170,172,175,178],{"class":70,"line":171},5,[68,173,174],{"class":109},"  tailwind",[68,176,177],{"class":161},":",[68,179,180],{"class":119}," {\n",[68,182,184,187,189,192],{"class":70,"line":183},6,[68,185,186],{"class":109},"    cssConfigPath",[68,188,177],{"class":161},[68,190,191],{"class":78}," 'app\u002Fassets\u002Fcss\u002Fmain.css'",[68,193,168],{"class":119},[68,195,197],{"class":70,"line":196},7,[68,198,199],{"class":119},"  },\n",[68,201,203,206,209],{"class":70,"line":202},8,[68,204,205],{"class":119},"}",[68,207,208],{"class":109},"))",[68,210,120],{"class":119},[10,212,213,214,217,218,221,222,227],{},"It's disabled by default. If your CSS lives at ",[65,215,216],{},"src\u002Fstyle.css",", ",[65,219,220],{},"tailwind: true"," is enough. The other ",[13,223,226],{"href":224,"rel":225},"https:\u002F\u002Fgithub.com\u002Ffrancoismassart\u002Feslint-plugin-tailwindcss#settings",[17],"plugin settings"," go in the same object.",[50,229,231],{"id":230},"upgrade-to-antfueslint-config-v10-and-eslint-10","Upgrade to @antfu\u002Feslint-config v10 and ESLint 10",[10,233,234,235,240],{},"3.0 also updates to ",[13,236,239],{"href":237,"rel":238},"https:\u002F\u002Fgithub.com\u002Fantfu\u002Feslint-config",[17],"@antfu\u002Feslint-config"," v10 and ESLint 10. Upgrade both packages:",[58,242,244],{"className":60,"code":243,"language":62,"meta":63,"style":63},"pnpm add -D eslint@latest @angelblanco\u002Feslint-config@latest\n",[65,245,246],{"__ignoreMap":63},[68,247,248,250,252,254,257],{"class":70,"line":71},[68,249,75],{"class":74},[68,251,79],{"class":78},[68,253,82],{"class":78},[68,255,256],{"class":78}," eslint@latest",[68,258,259],{"class":78}," @angelblanco\u002Feslint-config@latest\n",[10,261,262,263,266],{},"Expect ",[65,264,265],{},"eslint --fix"," to reformat some multi-line arrays, objects and ternaries the first time you run it.",[268,269],"github-repo-preview",{"description":270,"repo":271},"Ángel Blanco's ESLint configuration, built on @antfu\u002Feslint-config for Vue + TypeScript projects.","angelblanco\u002Feslint-config",[273,274,275],"style",{},"html pre.shiki code .sMmZK, html code.shiki .sMmZK{--shiki-default:#1E66F5;--shiki-default-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic}html pre.shiki code .sgueU, html code.shiki .sgueU{--shiki-default:#40A02B;--shiki-dark:#A6E3A1}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sAS4m, html code.shiki .sAS4m{--shiki-default:#8839EF;--shiki-dark:#CBA6F7}html pre.shiki code .s0aCy, html code.shiki .s0aCy{--shiki-default:#4C4F69;--shiki-dark:#CDD6F4}html pre.shiki code .sxyLT, html code.shiki .sxyLT{--shiki-default:#7C7F93;--shiki-dark:#9399B2}html pre.shiki code .s2f33, html code.shiki .s2f33{--shiki-default:#E64553;--shiki-default-font-style:italic;--shiki-dark:#EBA0AC;--shiki-dark-font-style:italic}html pre.shiki code .sjJjI, html code.shiki .sjJjI{--shiki-default:#179299;--shiki-dark:#94E2D5}html pre.shiki code .seXix, html code.shiki .seXix{--shiki-default:#E64553;--shiki-dark:#EBA0AC}",{"title":63,"searchDepth":123,"depth":123,"links":277},[278,279],{"id":52,"depth":123,"text":53},{"id":230,"depth":123,"text":231},"2026-11-07","@angelblanco\u002Feslint-config 3.0 is available on npm: optional Tailwind CSS v4 linting, @antfu\u002Feslint-config v10 and ESLint 10.","md",null,{},"\u002Fblog\u002Flint-tailwind-css-v4-with-eslint","2",{"title":5,"description":281},"blog\u002F0006.lint-tailwind-css-v4-with-eslint",[290,291,292,293],"eslint","tailwindcss","tooling","npm",false,"FSzUArCYkVVm935pw4kbUyZ23SOtOEjrUS_BePcHkBA",{"id":297,"title":298,"body":299,"date":280,"description":456,"extension":282,"image":283,"meta":457,"navigation":126,"path":458,"readingTime":286,"schemaOrg":283,"seo":459,"stem":460,"tags":461,"toc":294,"updatedAt":283,"__hash__":462},"blog_es\u002Fes\u002Fblog\u002F0006.lint-tailwind-css-v4-with-eslint.md","@angelblanco\u002Feslint-config 3.0 ya disponible, ahora con Tailwind CSS",{"type":7,"value":300,"toc":452},[301,311,327,330,334,337,351,357,399,413,417,424,440,446,449],[10,302,303,306,307,310],{},[13,304,18],{"href":15,"rel":305},[17]," ya está disponible en npm. Ahora puedes pasar el linter por tus clases de ",[13,308,24],{"href":22,"rel":309},[17],".",[10,312,313,314,318,319,322,323,326],{},"Cuando empecé este blog y ",[13,315,317],{"href":316},"\u002Fes\u002Fblog\u002Fcreate-your-eslint-config\u002F","creé mi propia configuración de ESLint"," en 2025, todavía no había un plugin de ",[13,320,38],{"href":36,"rel":321},[17]," para Tailwind CSS v4. En noviembre de 2026 estoy actualizando mis repositorios open source, y ",[13,324,44],{"href":42,"rel":325},[17]," por fin está listo.",[10,328,329],{},"Es impresionante cuánto puede cambiar en un año el stack que usas, pero de eso escribiré en próximos artículos.",[50,331,333],{"id":332},"activa-el-lint-de-tailwind-css","Activa el lint de Tailwind CSS",[10,335,336],{},"Instala el plugin (necesita Tailwind CSS v4):",[58,338,339],{"className":60,"code":61,"language":62,"meta":63,"style":63},[65,340,341],{"__ignoreMap":63},[68,342,343,345,347,349],{"class":70,"line":71},[68,344,75],{"class":74},[68,346,79],{"class":78},[68,348,82],{"class":78},[68,350,85],{"class":78},[10,352,353,354,356],{},"Después activa la opción ",[65,355,91],{}," e indícale tu fichero CSS de entrada:",[58,358,359],{"className":95,"code":96,"filename":97,"language":98,"meta":63,"style":63},[65,360,361,366,370,375,380,385,390,394],{"__ignoreMap":63},[68,362,363],{"class":70,"line":71},[68,364,365],{},"import eslintConfig from '@angelblanco\u002Feslint-config';\n",[68,367,368],{"class":70,"line":123},[68,369,127],{"emptyLinePlaceholder":126},[68,371,372],{"class":70,"line":130},[68,373,374],{},"export default eslintConfig(defaultOptions => ({\n",[68,376,377],{"class":70,"line":158},[68,378,379],{},"  ...defaultOptions,\n",[68,381,382],{"class":70,"line":171},[68,383,384],{},"  tailwind: {\n",[68,386,387],{"class":70,"line":183},[68,388,389],{},"    cssConfigPath: 'app\u002Fassets\u002Fcss\u002Fmain.css',\n",[68,391,392],{"class":70,"line":196},[68,393,199],{},[68,395,396],{"class":70,"line":202},[68,397,398],{},"}));\n",[10,400,401,402,404,405,407,408,412],{},"Viene desactivado por defecto. Si tu CSS está en ",[65,403,216],{},", basta con ",[65,406,220],{},". El resto de ",[13,409,411],{"href":224,"rel":410},[17],"ajustes del plugin"," van en el mismo objeto.",[50,414,416],{"id":415},"actualiza-a-antfueslint-config-v10-y-eslint-10","Actualiza a @antfu\u002Feslint-config v10 y ESLint 10",[10,418,419,420,423],{},"La 3.0 también actualiza a ",[13,421,239],{"href":237,"rel":422},[17]," v10 y ESLint 10. Instala la última versión de ambos paquetes:",[58,425,426],{"className":60,"code":243,"language":62,"meta":63,"style":63},[65,427,428],{"__ignoreMap":63},[68,429,430,432,434,436,438],{"class":70,"line":71},[68,431,75],{"class":74},[68,433,79],{"class":78},[68,435,82],{"class":78},[68,437,256],{"class":78},[68,439,259],{"class":78},[10,441,442,443,445],{},"La primera vez que ejecutes ",[65,444,265],{}," verás que reformatea algunos arrays, objetos y ternarios multilínea.",[268,447],{"description":448,"repo":271},"Configuración de ESLint de Ángel Blanco, construida sobre @antfu\u002Feslint-config para proyectos Vue + TypeScript.",[273,450,451],{},"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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMmZK, html code.shiki .sMmZK{--shiki-default:#1E66F5;--shiki-default-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic}html pre.shiki code .sgueU, html code.shiki .sgueU{--shiki-default:#40A02B;--shiki-dark:#A6E3A1}",{"title":63,"searchDepth":123,"depth":123,"links":453},[454,455],{"id":332,"depth":123,"text":333},{"id":415,"depth":123,"text":416},"@angelblanco\u002Feslint-config 3.0 ya está en npm: lint opcional de Tailwind CSS v4, @antfu\u002Feslint-config v10 y ESLint 10.",{},"\u002Fes\u002Fblog\u002Flint-tailwind-css-v4-with-eslint",{"title":298,"description":456},"es\u002Fblog\u002F0006.lint-tailwind-css-v4-with-eslint",[290,291,292,293],"40hwZlwShaqUR-Tmj-2YmvXDxrkE9iMpSV3hqkGvnWQ",[464,466,477,487,497,505],{"id":4,"path":285,"title":5,"description":281,"date":280,"readingTime":286,"tags":465},[290,291,292,293],{"id":467,"path":468,"title":469,"description":470,"date":471,"readingTime":472,"tags":473},"blog_en\u002Fblog\u002F0005.apt-install-php-85-on-ubuntu-26.md","\u002Fblog\u002Fapt-install-php-85-on-ubuntu-26","Install PHP 8.5 with apt on Ubuntu 26.04","Install PHP 8.5 and Composer on Ubuntu 26.04 LTS with apt from Ondřej Surý's packages.sury.org repository, and keep them patched with apt upgrade.","2026-11-06","11",[474,475,476,62],"php","ubuntu","composer",{"id":478,"path":479,"title":480,"description":481,"date":482,"readingTime":483,"tags":484},"blog_en\u002Fblog\u002F0004.apt-install-node-26-on-ubuntu-26.md","\u002Fblog\u002Fapt-install-node-26-on-ubuntu-26","Install Node.js 26 with apt on Ubuntu 26.04","On October 21, 2026, Node.js released security patches for ten vulnerabilities affecting its supported versions, four of them rated high, including a remote code execution issue. Unsupported versions, such as Node.js 20 and 25, won't be updated.","2026-11-05","7",[485,475,486,62],"nodejs","javascript",{"id":488,"path":489,"title":490,"description":491,"date":492,"readingTime":493,"tags":494},"blog_en\u002Fblog\u002F0003.create-your-eslint-config.md","\u002Fblog\u002Fcreate-your-eslint-config","Create your own ESLint config and share it across projects","If you've ever worked on multiple projects, you know the pain of copying ESLint configurations between repositories. Every time you set up a new project, you either start from scratch or copy the eslint.config.js file from your last one. There's a better way: publish your own shareable ESLint config package.","2025-11-02","3",[290,495,496,292,293],"typescript","vue",{"id":498,"path":499,"title":500,"description":501,"date":502,"readingTime":503,"tags":504},"blog_en\u002Fblog\u002F0002.install-node-24-on-ubuntu-24.md","\u002Fblog\u002Finstall-node-24-on-ubuntu-24","Install Node.js 24 on Ubuntu 24.04","Getting Node.js 24 running on Ubuntu 24 should be a straightforward process. While Ubuntu comes with a version of Node.js in its default repositories, you might want to use Node.js 24, which became LTS on October 28, 2025 and is supported until April 2028. For a new setup, install Node.js 26 on Ubuntu 26.04 instead. This guide offers two simple methods to get you set up quickly.","2025-10-12","6",[485,475,486,62],{"id":506,"path":507,"title":508,"description":509,"date":510,"readingTime":511,"tags":512},"blog_en\u002Fblog\u002F0001.hello.md","\u002Fblog\u002Fhello","From Svelte to Nuxt: Rebuilding my portfolio in 2025","It's 2025, and I've just rolled out a complete rebuild of my personal website. I've migrated from a Svelte and Sapper stack to Nuxt, Tailwind CSS and a Turborepo monorepo. It wasn't a cosmetic update but a full rewrite, driven by five years of changes in the frontend landscape and by the wish to use the same tools at home as at work.","2025-09-27","4",[513,496,514,515,291],"nuxt","svelte","turborepo",1794096649858]