=item publicinbox.css
The local path name of a CSS file for the PSGI web interface.
-May contain the attributes "media", "title" and "href" which match
-the associated attributes of the HTML <style> tag.
-"href" may be specified to point to the URL of a remote CSS file
+Space-separated attributes (C<key=val> or C<key='val with spaces'>)
+may be specified after the path on the same line; C<media>, C<title>
+and C<href> are currently supported, and match the associated attributes
+of the HTML C<E<lt>styleE<gt>> tag.
+
+C<href> may be specified to point to the URL of a remote CSS file
and the path may be "/dev/null" or any empty file.
+
+public-inbox 2.0+ also supports a C<load> attribute with values
+C<link> or C<import>; C<link> forces the use of a C<E<lt>linkE<gt>> tag
+and disables inlining CSS into the C<E<lt>styleE<gt>> tag while
+C<import> puts CSS C<@import> statements into the C<E<lt>styleE<gt>> tag.
+C<import> appears necessary for some browser and C<media> query combinations.
+
Multiple files may be specified and will be included in the
-order specified.
+given order, but files specified via C<load=import> are
+always handled first.
+
+Example:
+
+ [publicinbox]
+ css = /path/to/216dark.css title=216dark \
+ media='screen,(prefers-color-scheme:dark)'
+
+See the L<contrib/css
+directory of the public-inbox
+source|https://80x24.org/public-inbox.git/tree/contrib/css>
+for more examples.
=item publicinboxImport.dropUniqueUnsubscribe
my $stylesheets = $self->{pi_cfg}->{css} || [];
my $links = [];
my $inline_ok = 1;
- my (%css_dir, @css_dir);
+ my (%css_dir, @css_dir, $import);
foreach my $s (@$stylesheets) {
my $attr = {};
local $_ = $s;
- foreach my $k (qw(media title href)) {
+ foreach my $k (qw(media title href load)) {
if (s/\s*$k='([^']+)'// || s/\s*$k=(\S+)//) {
$attr->{$k} = $1;
}
}
-
+ # we may support `last' (to load CSS last at </html>)
+ # or other load directives in the future...
+ for my $l (split /,/, delete $attr->{load} // '') {
+ if ($l eq 'import') {
+ $inline_ok = 0;
+ $import = $attr->{-do_import} = 1;
+ } elsif ($l eq 'link') {
+ $inline_ok = 0;
+ } else {
+ warn "W: load=$l not recognized (ignored)\n";
+ }
+ }
if (defined $attr->{href}) {
$inline_ok = 0;
} else {
($local, $ctime) = @$rec;
} elsif (open(my $fh, '<', $fn)) {
($local, $ctime) = _read_css $fh, $mini, $fn;
- $local =~ /\@import\b/ && !$css_dir{$dir}++ and
- push @css_dir, $dir;
+ if ($local =~ /\@import\b/) {
+ $import = $attr->{-do_import} = 1;
+ push @css_dir, $dir if !$css_dir{$dir}++
+ }
$css_map->{$key} = [ $local, $ctime ];
} else {
warn "failed to open $fn: $!\n";
}
$attr->{href} = "$upfx$key.css?$ctime";
- if (defined($attr->{title})) {
+ if (defined($attr->{title})) { # browser-selectable
$inline_ok = 0;
} elsif (($attr->{media}||'screen') eq 'screen') {
$attr->{-inline} = $local;
}
push @$links, $attr;
}
-
- my $buf = "<style>$STYLE";
+ my $buf = '<style>';
+ if ($import) {
+ my @links;
+ for my $attr (@$links) {
+ if (delete $attr->{-do_import}) {
+ $buf .= '@import url("'.$attr->{href}.'") '.
+ $attr->{media}.';';
+ } else {
+ push @links, $attr;
+ }
+ }
+ $links = \@links;
+ }
+ # can't have $STYLE before @import(?)
+ # <https://developer.mozilla.org/en-US/docs/Web/CSS/@import>
+ $buf .= $STYLE;
if ($inline_ok) {
my @ext; # for media=print and whatnot
foreach my $attr (@$links) {