Tailwind_pl is a pack for SWI-Prolog that lets you do CSS styling with TailwindCSS without any Node.js dependency. It is effectively a rewrite of Tailwind's JIT compiler in Prolog, inspired by Girouette's approach in Clojure.
Tailwind_pl watches the given directories for changes to Prolog files, searches for Tailwind-style CSS selectors and generates a CSS file containing just the used selectors.
Tailwind_pl also extends Tailwind by allowing more dynamic class-names that use exact colours or numbers (ex. w-42%
).
?- pack_install(tailwind_pl).
:- module(myapp, [go/1]).
:- use_module(library(filesex), [directory_file_path/3, make_directory_path/1]).
:- use_module(library(http/http_dispatch), [http_redirect/3,
http_dispatch/1,
http_reply_file/3,
http_handler/3,
http_location_by_id/2
]).
:- use_module(library(http/html_write), [html//1,
reply_html_page/2,
html_receive//1]).
:- use_module(library(http/thread_httpd), [http_server/2,
http_stop_server/2]).
:- use_module(library(tailwind), [start_watching_dirs/3,
stop_watching_dirs/1,
reset_style/1]).
%! go(+Port) is det.
% Interactive entry point to start the server.
go(Port) :-
start_css_watcher,
http_server(http_dispatch, [port(Port)]).
user:file_search_path(css, CssDir) :-
working_directory(Cwd, Cwd),
directory_file_path(Cwd, "../resources/css", CssDir).
:- dynamic css_watcher/1.
start_css_watcher :-
stop_css_watcher,
working_directory(Cwd, Cwd),
directory_file_path(Cwd, "../resources/css", CssDir),
( exists_directory(CssDir) -> true ; make_directory_path(CssDir) ),
absolute_file_name(css("styles.css"), CssFile, [access(none)]),
start_watching_dirs([Cwd], CssFile, Watcher),
assertz(css_watcher(Watcher)).
stop_css_watcher :-
css_watcher(Watcher), !,
stop_watching_dirs(Watcher),
retractall(css_watcher(Watcher)).
stop_css_watcher.
% Routes
:- multifile http:location/3.
:- dynamic http:location/3.
http:location(css, root(css), []).
:- http_handler(root(.), home_page_handler, [method(get), id(home_page),
spawn(handler_pool)]).
:- http_handler(css('styles.css'), css_file_handler, [method(get), id(tw_css)]).
:- http_handler(css('reset.css'), reset_css_handler, [method(get), id(reset_css)]).
home_page_handler(_Request) :-
reply_html_page(title('Hotwire.pl'), [\home_page]).
css_file_handler(Request) :-
http_reply_file(css('styles.css'), [], Request).
reset_css_handler(_) :-
reset_style(Style),
format("Content-Type: text/css~n~n~s", [Style]).
% content
user:head(default, Head) -->
html([Head,
\html_receive(head),
link([rel(stylesheet), href(#(reset_css))], []),
link([rel(stylesheet), href(#(tw_css))], [])
]).
% Home Page
home_page -->
html(
div(class(["flex-col", "text-purple-500", 'bg-rose-50']),
[h2(class(['text-lg',
'hover:animation-ping',
'bg-green-300', 'hover:text-rose-100']),
"Info"),
\p("here's some stuff, some information"),
\p("Another paragraph")
])
).
p(Text) -->
html(p(class(['text-sm', 'text-blue', 'hover:opacity-50']),
Text)).
All the selectors from Tailwind should also be supported here. If any don't work, please file a bug!
Additionally, some new features are supported, taking advantage of the fact that the styles are created dynamically as needed.
-
Colours can be specified not just as the predefined colour names (e.g.
pink-500
) but as RGB(A) and HSL(A); for example, all of the following are valid styles:text-#c0ffee
,bg-rgb-cafeba
,from-hsla-50-60-70-0.5
. This feature is copied from Girouette. -
in most places where a size is given as just a number in Tailwind (e.g.
mx-4
which will set the left and right margins to1rem
), you can use arbitrary distances -- e.g.mx-2.3rem,
font-size-17.5px`. This feature is copied from Girouette. -
Similar to the
group-hover:
,group-focus:
, etc, you can usegroup-attr-SOMEATTR:
which will effective do.group[SOMEATTR]
, asgroup-hover
does.group:hover
. This can be used, for example, to do put thegroup
class on a<details>
element andgroup-attr-open:hidden
on some child, do hide said element when the detail element is open