If you’re using Elm with Tailwind CSS in NeoVim, you’ve probably noticed that IntelliSense doesn’t work out of the box. Here’s a quick recipe to fix that.
The Problem Link to heading
Tailwind’s LSP doesn’t recognize Elm’s syntax for CSS classes. When you write:
div [ class "bg-blue-500 text-white p-4" ] [ text "Hello" ]
You get no autocomplete, no validation, and no hover documentation for your Tailwind classes.
The Solution Link to heading
My Setup (LazyVim) Link to heading
Here’s what works for me using LazyVim. If you’re using a different NeoVim distribution, your mileage may vary:
{
"neovim/nvim-lspconfig",
opts = {
servers = {
tailwindcss = {
filetypes_include = { "elm" },
-- exclude a filetype from the default_config
filetypes_exclude = {},
},
},
setup = {
tailwindcss = function(_, opts)
local tw = LazyVim.lsp.get_raw_config("tailwindcss")
opts.filetypes = opts.filetypes or {}
-- Add default filetypes
vim.list_extend(opts.filetypes, tw.default_config.filetypes)
-- Remove excluded filetypes
opts.filetypes = vim.tbl_filter(function(ft)
return not vim.tbl_contains(opts.filetypes_exclude or {}, ft)
end, opts.filetypes)
opts.settings = {
tailwindCSS = {
includeLanguages = {
elm = "html",
},
experimental = {
classRegex = {
{ [[\bclass[\s(<|]+"([^"]*)"]] },
{ [[\bclass[\s(]+"[^"]*"\s+"([^"]*)"]] },
{ [[\bclass[\s<|]+"[^"]*"\s*\+{2}\s*" ([^"]*)"]] },
{ [[\bclass[\s<|]+"[^"]*"\s*\+{2}\s*" [^"]*"\s*\+{2}\s*" ([^"]*)"]] },
{ [[\bclass[\s<|]+"[^"]*"\s*\+{2}\s*" [^"]*"\s*\+{2}\s*" [^"]*"\s*\+{2}\s*" ([^"]*)"]] },
{ [[\bclassList[\s\[\(]+"([^"]*)"]] },
{ [[\bclassList[\s\[\(]+"[^"]*",\s[^\)]+\)[\s\[\(,]+"([^"]*)"]] },
{ [[\bclassList[\s\[\(]+"[^"]*",\s[^\)]+\)[\s\[\(,]+"[^"]*",\s[^\)]+\)[\s\[\(,]+"([^"]*)"]] },
},
},
},
}
-- Add additional filetypes
vim.list_extend(opts.filetypes, opts.filetypes_include or {})
end,
},
},
}
For Vanilla NeoVim Link to heading
If you’re using plain nvim-lspconfig, I’m guessing you’d need something like this (but I’m mostly spitballing here - you get the idea):
require('lspconfig').tailwindcss.setup({
filetypes = {
'html', 'css', 'javascript', 'typescript', 'react', 'vue', 'svelte', 'elm'
},
settings = {
tailwindCSS = {
includeLanguages = {
elm = "html",
},
experimental = {
classRegex = {
{ [[\bclass[\s(<|]+"([^"]*)"]] },
{ [[\bclass[\s(]+"[^"]*"\s+"([^"]*)"]] },
{ [[\bclass[\s<|]+"[^"]*"\s*\+{2}\s*" ([^"]*)"]] },
{ [[\bclass[\s<|]+"[^"]*"\s*\+{2}\s*" [^"]*"\s*\+{2}\s*" ([^"]*)"]] },
{ [[\bclass[\s<|]+"[^"]*"\s*\+{2}\s*" [^"]*"\s*\+{2}\s*" [^"]*"\s*\+{2}\s*" ([^"]*)"]] },
{ [[\bclassList[\s\[\(]+"([^"]*)"]] },
{ [[\bclassList[\s\[\(]+"[^"]*",\s[^\)]+\)[\s\[\(,]+"([^"]*)"]] },
{ [[\bclassList[\s\[\(]+"[^"]*",\s[^\)]+\)[\s\[\(,]+"[^"]*",\s[^\)]+\)[\s\[\(,]+"([^"]*)"]] },
},
},
},
},
})
What’s Happening Link to heading
Both configs do the same thing with different syntax:
- Add
elm
to filetypes - Tells Tailwind LSP to activate for Elm files includeLanguages = { elm = "html" }
- Treats Elm syntax like HTML for class detectionclassRegex
patterns - Regular expressions that match Elm’s various class syntax patterns:class "bg-blue-500"
- Basic class attributeclass "text-lg" ++ " font-bold"
- String concatenationclassList [ ("active", isActive) ]
- Conditional classes
The Result Link to heading
Now you get full Tailwind IntelliSense in your Elm files:
- Autocomplete for class names
- Hover documentation
- Color previews
- Validation warnings for invalid classes
Full Setup Link to heading
My complete NeoVim config (including this setup) is available at github.com/cekrem/dotfiles if you want to see how it fits into a larger LazyVim configuration.
This small addition makes Elm + Tailwind development much more pleasant. No more guessing at class names or checking the docs constantly.