1
Fork 0

Improve styling of aside elements

This commit is contained in:
prescientmoon 2024-11-04 23:27:21 +01:00
parent f7559c02e1
commit 379a265414
Signed by: prescientmoon
SSH key fingerprint: SHA256:WFp/cO76nbarETAoQcQXuV+0h7XJsEsOCI0UsyPIy6U
6 changed files with 166 additions and 232 deletions

View file

@ -5,17 +5,18 @@ body {
padding: 1em; padding: 1em;
} }
.article-content {
max-width: 40em;
margin: auto;
}
/* {{{ General element tweaks*/
blockquote { blockquote {
padding-left: 1.25rem; padding-left: 1.25rem;
border-left: 3px solid; border-left: 3px solid;
margin-left: 0; margin-left: 0;
} }
ul,
ol {
padding-left: 1rem;
}
h1, h1,
h2, h2,
h3, h3,
@ -30,33 +31,45 @@ h6 {
} }
} }
.article-content { math[display="block"] {
max-width: 40em; margin: 1.5em 0;
margin: auto;
} }
/* }}}*/
/* {{{ Asides*/ /* {{{ Asides */
.aside { /* TODO: remove aside-header altogether */
margin: 1.5rem 0;
}
/* {{{ Header*/
.aside-header { .aside-header {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
} }
.aside-header > .aside-title { .aside-header > img {
text-decoration: underline; height: 1.75em;
font-weight: bold; margin-right: 0.75em;
} }
.aside-header > * { .aside {
padding: 0; background: #f4daf7;
margin: 0; border-radius: 3px;
box-sizing: border-box;
padding-left: 1rem;
padding-right: 1rem;
margin: 1.5rem 0;
/* Prevents margin-collapsing of the children. */
border: 1px solid transparent;
} }
/* }}}*/
.aside-long > :not(summary) {
margin-left: 1rem;
}
.aside-long:has-child(.aside-header) > :not(summary):nth-child(2) {
margin-top: 0;
padding-top: 0;
}
/* {{{ Override marker*/ /* {{{ Override marker*/
.aside-summary::marker { .aside-summary::marker {
display: none; display: none;
@ -74,34 +87,8 @@ h6 {
} }
/* }}}*/ /* }}}*/
img.aside-icon {
height: 1.75rem;
margin-right: 0.75rem;
}
.aside {
background: #f4daf7;
border-radius: 3px;
box-sizing: border-box;
padding: 1rem;
}
.aside-short {
padding: 1rem;
}
.aside-long {
/* padding: 0.65rem; */
.aside-content {
padding-left: 1rem;
}
}
/* }}}*/ /* }}}*/
/* {{{ Tier lists*/ /* {{{ Tier lists */
.tier-list { .tier-list {
background: #444; background: #444;

View file

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::anyhow; use anyhow::anyhow;
use anyhow::bail;
use chrono::DateTime; use chrono::DateTime;
use chrono::TimeZone; use chrono::TimeZone;
use jotdown::Alignment; use jotdown::Alignment;
@ -77,13 +78,14 @@ impl<'s> Writer<'s> {
return Ok(()); return Ok(());
} }
// }}} // }}}
// {{{ Handle blocks which trigger the `Ignore` state. // {{{ Handle important state changes
match e { match e {
Event::Start(Container::LinkDefinition { .. }, ..) => { Event::Start(Container::LinkDefinition { .. }, ..) => {
self.states.push(State::Ignore); self.states.push(State::Ignore);
return Ok(()); return Ok(());
} }
Event::End(Container::LinkDefinition { .. }) => { Event::End(Container::LinkDefinition { .. }) => {
// Sanity check
assert!(matches!(self.states.last(), Some(State::Ignore))); assert!(matches!(self.states.last(), Some(State::Ignore)));
self.states.pop(); self.states.pop();
} }
@ -97,12 +99,12 @@ impl<'s> Writer<'s> {
return Ok(()); return Ok(());
} }
Event::End(Container::RawBlock { format } | Container::RawInline { format }) => { Event::End(Container::RawBlock { .. } | Container::RawInline { .. }) => {
if format == &"html" { // Sanity check
assert!(matches!(self.states.last(), Some(State::Raw))); assert!(matches!(
} else { self.states.last(),
assert!(matches!(self.states.last(), Some(State::Ignore))); Some(State::Raw | State::Ignore)
}; ));
self.states.pop(); self.states.pop();
} }
@ -126,79 +128,23 @@ impl<'s> Writer<'s> {
Container::RawBlock { .. } => {} Container::RawBlock { .. } => {}
Container::RawInline { .. } => unreachable!(), Container::RawInline { .. } => unreachable!(),
Container::Footnote { .. } => unreachable!(), Container::Footnote { .. } => unreachable!(),
// {{{ List // {{{ Section
Container::List { kind, tight } => {
self.list_tightness.push(*tight);
match kind {
ListKind::Unordered(..) | ListKind::Task(..) => out.write_str("<ul")?,
ListKind::Ordered {
numbering, start, ..
} => {
out.write_str("<ol")?;
if *start > 1 {
write!(out, r#" start="{}""#, start)?;
}
if let Some(ty) = match numbering {
Decimal => None,
AlphaLower => Some('a'),
AlphaUpper => Some('A'),
RomanLower => Some('i'),
RomanUpper => Some('I'),
} {
write!(out, r#" type="{}""#, ty)?;
}
}
}
}
// }}}
// {{{ Link
Container::Link(dst, ty) => {
if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
out.write_str("<a")?;
} else {
out.write_str(r#"<a href=""#)?;
if matches!(ty, LinkType::Email) {
out.write_str("mailto:")?;
}
write_attr_contents(dst, &mut out)?;
out.write_char('"')?;
}
}
// }}}
// {{{ Paragraph
Container::Paragraph => {
if self.list_tightness.last() == Some(&true) {
return Ok(());
}
out.write_str("<p")?;
}
// }}}
Container::Blockquote => out.write_str("<blockquote")?,
Container::ListItem { .. } => out.write_str("<li")?,
Container::TaskListItem { .. } => out.write_str("<li")?,
Container::DescriptionList => out.write_str("<dl")?,
Container::DescriptionDetails => out.write_str("<dd")?,
Container::Table => out.write_str("<table")?,
Container::TableRow { .. } => out.write_str("<tr")?,
Container::Section { id } => match self.metadata { Container::Section { id } => match self.metadata {
Some(meta) Some(meta)
if &meta.title.id == id && matches!(meta.route, PageRoute::Post(_)) => if &meta.title.id == id && matches!(meta.route, PageRoute::Post(_)) =>
{ {
let renderer = template!("templates/post.html", &mut out)?; let renderer = template!("templates/post.html", &mut out)?;
// Sanity check
assert_eq!(renderer.current(), Some("attrs")); assert_eq!(renderer.current(), Some("attrs"));
self.states.push(State::Article(renderer)); self.states.push(State::Article(renderer));
} }
_ => out.write_str("<section")?, _ => out.write_str("<section")?,
}, },
// }}}
// {{{ Aside
Container::Div { Container::Div {
class: class @ ("aside" | "long-aside" | "char-aside"), class: class @ ("aside" | "long-aside" | "char-aside"),
} => { } => {
if *class == "aside" {
self.list_tightness.push(true);
}
let mut renderer = if *class == "aside" { let mut renderer = if *class == "aside" {
template!("templates/aside.html", &mut out)? template!("templates/aside.html", &mut out)?
} else if *class == "char-aside" { } else if *class == "char-aside" {
@ -233,6 +179,63 @@ impl<'s> Writer<'s> {
self.states.push(State::Aside(renderer)); self.states.push(State::Aside(renderer));
} }
// }}}
// {{{ List
Container::List { kind, tight } => {
self.list_tightness.push(*tight);
match kind {
ListKind::Unordered(..) => out.write_str("<ul")?,
ListKind::Ordered {
numbering, start, ..
} => {
out.write_str("<ol")?;
if *start > 1 {
write!(out, r#" start="{}""#, start)?;
}
if let Some(ty) = match numbering {
Decimal => None,
AlphaLower => Some('a'),
AlphaUpper => Some('A'),
RomanLower => Some('i'),
RomanUpper => Some('I'),
} {
write!(out, r#" type="{}""#, ty)?;
}
}
ListKind::Task(_) => bail!("Task lists are not supported"),
}
}
// }}}
// {{{ Link
Container::Link(dst, ty) => {
if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
out.write_str("<a")?;
} else {
out.write_str(r#"<a href=""#)?;
if matches!(ty, LinkType::Email) {
out.write_str("mailto:")?;
}
write_attr_contents(dst, &mut out)?;
out.write_char('"')?;
}
}
// }}}
// {{{ Paragraph
Container::Paragraph => {
if self.list_tightness.last() == Some(&true) {
return Ok(());
}
out.write_str("<p")?;
}
// }}}
Container::Blockquote => out.write_str("<blockquote")?,
Container::ListItem { .. } => out.write_str("<li")?,
Container::DescriptionList => out.write_str("<dl")?,
Container::DescriptionDetails => out.write_str("<dd")?,
Container::Table => out.write_str("<table")?,
Container::TableRow { .. } => out.write_str("<tr")?,
Container::Div { .. } => out.write_str("<div")?, Container::Div { .. } => out.write_str("<div")?,
Container::Heading { level, .. } => write!(out, "<h{}", level)?, Container::Heading { level, .. } => write!(out, "<h{}", level)?,
Container::TableCell { head: false, .. } => out.write_str("<td")?, Container::TableCell { head: false, .. } => out.write_str("<td")?,
@ -251,8 +254,10 @@ impl<'s> Writer<'s> {
Container::Emphasis => out.write_str("<em")?, Container::Emphasis => out.write_str("<em")?,
Container::Mark => out.write_str("<mark")?, Container::Mark => out.write_str("<mark")?,
Container::LinkDefinition { .. } => return Ok(()), Container::LinkDefinition { .. } => return Ok(()),
e => bail!("DJot element {e:?} is not supported"),
} }
// {{{ Decide whether this element supports attributes
let mut write_attr_contentsibs = true; let mut write_attr_contentsibs = true;
if matches!( if matches!(
c, c,
@ -262,6 +267,7 @@ impl<'s> Writer<'s> {
) { ) {
write_attr_contentsibs = false; write_attr_contentsibs = false;
} }
// }}}
if write_attr_contentsibs { if write_attr_contentsibs {
// {{{ Write attributes // {{{ Write attributes
@ -293,26 +299,12 @@ impl<'s> Writer<'s> {
write_attr("aria-labeledby", id, &mut out)?; write_attr("aria-labeledby", id, &mut out)?;
} }
Container::Div { class } if !class.is_empty() && !class_written => { Container::Div { class } if !class.is_empty() && !class_written => {
out.write_str(r#" class=""#)?; write_attr("class", class, &mut out)?;
write_class(c, false, &mut out)?;
out.write_char('"')?;
}
Container::Math { .. }
| Container::List {
kind: ListKind::Task(..),
..
}
| Container::TaskListItem { .. }
if !class_written =>
{
out.write_str(r#" class=""#)?;
write_class(c, false, &mut out)?;
out.write_char('"')?;
} }
_ => {} _ => {}
} }
// }}} // }}}
// {{{ Write special attributes
match c { match c {
// {{{ Write css for aligning table cell text // {{{ Write css for aligning table cell text
Container::TableCell { alignment, .. } Container::TableCell { alignment, .. }
@ -352,8 +344,10 @@ impl<'s> Writer<'s> {
_ => out.write_char('>')?, _ => out.write_char('>')?,
}, },
} }
// }}}
} }
// {{{ Post-start effects
match &c { match &c {
Container::Heading { id, .. } => { Container::Heading { id, .. } => {
out.write_str(r##"<a href="#"##)?; out.write_str(r##"<a href="#"##)?;
@ -365,17 +359,21 @@ impl<'s> Writer<'s> {
} }
_ => {} _ => {}
} }
// }}}
} }
// }}} // }}}
// {{{ Container end // {{{ Container end
Event::End(c) => { Event::End(c) => {
// {{{ Pre-end effects
match &c { match &c {
Container::Image(..) => { Container::Image(..) => {
// Sanity check
assert!(matches!(self.states.last(), Some(State::TextOnly))); assert!(matches!(self.states.last(), Some(State::TextOnly)));
self.states.pop(); self.states.pop();
} }
_ => {} _ => {}
} }
// }}}
if matches!(self.states.last(), Some(State::TextOnly)) { if matches!(self.states.last(), Some(State::TextOnly)) {
return Ok(()); return Ok(());
@ -389,10 +387,10 @@ impl<'s> Writer<'s> {
Container::List { kind, .. } => { Container::List { kind, .. } => {
self.list_tightness.pop(); self.list_tightness.pop();
match kind { match kind {
ListKind::Unordered(..) | ListKind::Task(..) => { ListKind::Unordered(..) => out.write_str("</ul>")?,
out.write_str("</ul>")?
}
ListKind::Ordered { .. } => out.write_str("</ol>")?, ListKind::Ordered { .. } => out.write_str("</ol>")?,
// We error out when the task list begins
ListKind::Task(..) => unreachable!(),
} }
} }
// }}} // }}}
@ -402,9 +400,7 @@ impl<'s> Writer<'s> {
return Ok(()); return Ok(());
} }
if !self.footnotes.in_epilogue() { out.write_str("</p>")?;
out.write_str("</p>")?;
}
} }
// }}} // }}}
// {{{ Image // {{{ Image
@ -419,18 +415,13 @@ impl<'s> Writer<'s> {
// }}} // }}}
// {{{ Math // {{{ Math
Container::Math { .. } => { Container::Math { .. } => {
// Sanity check
assert!(matches!(self.states.last(), Some(State::Math(_)))); assert!(matches!(self.states.last(), Some(State::Math(_))));
self.states.pop(); self.states.pop();
out.write_str(r#"</span>"#)?; out.write_str(r#"</span>"#)?;
} }
// }}} // }}}
Container::Blockquote => out.write_str("</blockquote>")?, // {{{ Section
Container::ListItem { .. } => out.write_str("</li>")?,
Container::TaskListItem { .. } => out.write_str("</li>")?,
Container::DescriptionList => out.write_str("</dl>")?,
Container::DescriptionDetails => out.write_str("</dd>")?,
Container::Table => out.write_str("</table>")?,
Container::TableRow { .. } => out.write_str("</tr>")?,
Container::Section { id, .. } => match self.metadata { Container::Section { id, .. } => match self.metadata {
Some(meta) Some(meta)
if &meta.title.id == id if &meta.title.id == id
@ -444,25 +435,25 @@ impl<'s> Writer<'s> {
} }
_ => out.write_str("</section>")?, _ => out.write_str("</section>")?,
}, },
// }}}
// {{{ Aside
Container::Div { Container::Div {
class: class @ ("aside" | "long-aside" | "char-aside"), class: "aside" | "long-aside" | "char-aside",
} => { } => {
if *class == "aside" {
self.list_tightness.pop();
}
let state = self.states.pop().unwrap(); let state = self.states.pop().unwrap();
let State::Aside(renderer) = state else { let State::Aside(renderer) = state else {
panic!("Finished `aside` element without being in the `Aside` state.") panic!("Finished `aside` element without being in the `Aside` state.")
}; };
// Sanity check
assert_eq!(renderer.current(), Some("content")); assert_eq!(renderer.current(), Some("content"));
renderer.finish(&mut out)?; renderer.finish(&mut out)?;
} }
Container::Div { .. } => out.write_str("</div>")?, // }}}
Container::Heading { level, .. } => { Container::Heading { level, .. } => {
write!(out, "</h{}>", level)?; write!(out, "</h{}>", level)?;
// {{{ Article title
if let Some(State::Article(renderer)) = self.states.last_mut() { if let Some(State::Article(renderer)) = self.states.last_mut() {
if renderer.current() == Some("title") { if renderer.current() == Some("title") {
// SAFETY: we can never enter into the `article` state without having // SAFETY: we can never enter into the `article` state without having
@ -516,7 +507,15 @@ impl<'s> Writer<'s> {
} }
} }
} }
// }}}
} }
Container::Blockquote => out.write_str("</blockquote>")?,
Container::ListItem { .. } => out.write_str("</li>")?,
Container::DescriptionList => out.write_str("</dl>")?,
Container::DescriptionDetails => out.write_str("</dd>")?,
Container::Table => out.write_str("</table>")?,
Container::TableRow { .. } => out.write_str("</tr>")?,
Container::Div { .. } => out.write_str("</div>")?,
Container::TableCell { head: false, .. } => out.write_str("</td>")?, Container::TableCell { head: false, .. } => out.write_str("</td>")?,
Container::TableCell { head: true, .. } => out.write_str("</th>")?, Container::TableCell { head: true, .. } => out.write_str("</th>")?,
Container::Caption => out.write_str("</caption>")?, Container::Caption => out.write_str("</caption>")?,
@ -533,6 +532,7 @@ impl<'s> Writer<'s> {
Container::Emphasis => out.write_str("</em>")?, Container::Emphasis => out.write_str("</em>")?,
Container::Mark => out.write_str("</mark>")?, Container::Mark => out.write_str("</mark>")?,
Container::LinkDefinition { .. } => unreachable!(), Container::LinkDefinition { .. } => unreachable!(),
e => bail!("DJot element {e:?} is not supported"),
} }
} }
// }}} // }}}
@ -540,6 +540,7 @@ impl<'s> Writer<'s> {
Event::Str(s) => match self.states.last() { Event::Str(s) => match self.states.last() {
Some(State::TextOnly) => write_attr_contents(s, &mut out)?, Some(State::TextOnly) => write_attr_contents(s, &mut out)?,
Some(State::Raw) => out.write_str(s)?, Some(State::Raw) => out.write_str(s)?,
// {{{ Math
Some(State::Math(display)) => { Some(State::Math(display)) => {
let config = pulldown_latex::RenderConfig { let config = pulldown_latex::RenderConfig {
display_mode: { display_mode: {
@ -562,7 +563,8 @@ impl<'s> Writer<'s> {
pulldown_latex::push_mathml(&mut mathml, parser, config).unwrap(); pulldown_latex::push_mathml(&mut mathml, parser, config).unwrap();
out.write_str(&mathml)?; out.write_str(&mathml)?;
} }
_ => write_text(s, &mut out)?, // }}}
_ => write_escape(s, false, &mut out)?,
}, },
// }}} // }}}
// {{{ Footnote reference // {{{ Footnote reference
@ -571,7 +573,7 @@ impl<'s> Writer<'s> {
if !matches!(self.states.last(), Some(State::TextOnly)) { if !matches!(self.states.last(), Some(State::TextOnly)) {
write!( write!(
out, out,
r##"<a id="fnref{}" href="#fn{}" role="doc-noteref"><sup>{}</sup></a>"##, r##"<sup><a id="fnref{}" href="#fn{}" role="doc-noteref">{}</a></sup>"##,
number, number, number number, number, number
)?; )?;
} }
@ -611,41 +613,24 @@ impl<'s> Writer<'s> {
// {{{ Render epilogue // {{{ Render epilogue
fn render_epilogue(&mut self, mut out: impl std::fmt::Write) -> anyhow::Result<()> { fn render_epilogue(&mut self, mut out: impl std::fmt::Write) -> anyhow::Result<()> {
if self.footnotes.reference_encountered() { if self.footnotes.reference_encountered() {
out.write_str("<section role=\"doc-endnotes\">")?; // TODO: rewrite this using a template
out.write_str("<hr>")?; out.write_str("<section role=\"doc-endnotes\"><hr><ol>")?;
out.write_str("<ol>")?;
while let Some((number, events)) = self.footnotes.next() { while let Some((number, events)) = self.footnotes.next() {
write!(out, "<li id=\"fn{}\">", number)?; write!(out, "<li id=\"fn{}\">", number)?;
let mut unclosed_para = false;
for e in events.iter().flatten() { for e in events.iter().flatten() {
if matches!(&e, Event::Blankline | Event::Escape) {
continue;
}
if unclosed_para {
// not a footnote, so no need to add href before para close
out.write_str("</p>")?;
}
self.render_event(e, &mut out)?; self.render_event(e, &mut out)?;
unclosed_para = matches!(e, Event::End(Container::Paragraph { .. }))
&& !matches!(self.list_tightness.last(), Some(true));
}
if !unclosed_para {
// create a new paragraph
out.write_str("<p>")?;
} }
write!( write!(
out, out,
"<a href=\"#fnref{}\" role=\"doc-backlink\">\u{21A9}\u{FE0E}</a></p>", "<a href=\"#fnref{}\" role=\"doc-backlink\">Return to content \u{21A9}\u{FE0E}</a></li>",
number, number,
)?; )?;
out.write_str("</li>")?;
} }
out.write_str("</ol>")?; out.write_str("</ol></section>")?;
out.write_str("</section>")?;
} }
Ok(()) Ok(())
@ -654,40 +639,6 @@ impl<'s> Writer<'s> {
} }
// {{{ Writing helpers // {{{ Writing helpers
fn write_class<W>(c: &Container, mut first_written: bool, out: &mut W) -> std::fmt::Result
where
W: std::fmt::Write,
{
if let Some(cls) = match c {
Container::List {
kind: ListKind::Task(..),
..
} => Some("task-list"),
Container::TaskListItem { checked: false } => Some("unchecked"),
Container::TaskListItem { checked: true } => Some("checked"),
Container::Math { display: false } => Some("math inline"),
Container::Math { display: true } => Some("math display"),
_ => None,
} {
first_written = true;
out.write_str(cls)?;
}
if let Container::Div { class } = c {
if !class.is_empty() {
if first_written {
out.write_char(' ')?;
}
out.write_str(class)?;
}
}
Ok(())
}
#[inline]
fn write_text(s: &str, out: impl std::fmt::Write) -> std::fmt::Result {
write_escape(s, false, out)
}
#[inline] #[inline]
fn write_attr_contents(s: &str, out: impl std::fmt::Write) -> std::fmt::Result { fn write_attr_contents(s: &str, out: impl std::fmt::Write) -> std::fmt::Result {
write_escape(s, true, out) write_escape(s, true, out)
@ -765,11 +716,6 @@ impl<'s> Footnotes<'s> {
!self.references.is_empty() !self.references.is_empty()
} }
/// Returns `true` if within the epilogue, i.e. if any footnotes have been pulled.
fn in_epilogue(&self) -> bool {
self.number > 0
}
/// Add a footnote reference. /// Add a footnote reference.
fn reference(&mut self, label: &'s str) -> usize { fn reference(&mut self, label: &'s str) -> usize {
self.references self.references

View file

@ -1 +1 @@
<div class="aside aside-short aside-content">{{content}}</div> <aside class="aside">{{content}}</aside>

View file

@ -1,11 +1,11 @@
<div class="aside aside-short"> <aside class="aside">
<div class="aside-header"> <div class="aside-header">
<img <img
class="aside-icon"
alt="{{character}}" alt="{{character}}"
src="/assets/icons/characters/{{character}}.webp" src="/assets/icons/characters/{{character}}.webp"
/> />
<span class="aside-title">{{title}}</span> <h3>{{title}}</h3>
</div> </div>
<div class="aside-content">{{content}}</div>
</div> {{content}}
</aside>

View file

@ -1,11 +1,12 @@
<details class="aside aside-long"> <aside>
<summary class="aside-summary aside-header"> <details class="aside aside-long">
<img <summary class="aside-summary aside-header">
class="aside-icon" <img
alt="{{character}}" alt="{{character}}"
src="/assets/icons/characters/{{character}}.webp" src="/assets/icons/characters/{{character}}.webp"
/> />
<span class="aside-title">{{title}}</span> <h3>{{title}}</h3>
</summary> </summary>
<div class="aside-content">{{content}}</div> {{content}}
</details> </details>
</aside>

View file

@ -5,7 +5,7 @@
<ul> <ul>
<li> <li>
{{posted_on}} by <a href="about:blank">prescientmoon</a> on their {{posted_on}} by <a href="about:blank">prescientmoon</a> on their
<a href="moonythm.dev">website</a> <a href="https://moonythm.dev">website</a>
</li> </li>
<li> <li>
Last updated on {{updated_on}}. <a href="about:blank">Source</a> | Last updated on {{updated_on}}. <a href="about:blank">Source</a> |