/// An *opinionated* template for a longer report or thesis. #let tasteful-thesis( /// The main title of the document. /// -> string title: "", /// An optional subtitle for the document. /// -> string subtitle: "", /// The authors of the document. /// -> array(string) authors: (), supervisors: (), affiliation: none, other: none, abstract: none, background-image: none, date: datetime.today().display(), background-color: color.blue, logos: (), font: "New Computer Modern", alpha: 50%, pre_content: none, body, ) = { // // Global settings and style customization. // set document(author: authors, title: title, description: subtitle) // Set body font family. set text(font: font, 11pt) show heading: set text(font: font, fill: background-color) let font-color = color; // Check if the background color is closer to black or white let components = background-color.components() let luminance = float(0.299 * components.at(0) + 0.587 * components.at(1) + 0.114 * components.at(2)) if luminance > 0.5 { font-color = color.black } else { font-color = color.white } // color links show link: it => underline(text(fill: background-color, it)) show ref: it => text(fill: background-color, it) show ref.where(): it => text(fill: background-color, it) // colors lists set enum(indent: 1em, numbering: n => [#text(fill: background-color, numbering("1.", n))]) set list(indent: 1em, marker: n => [#text(fill: background-color, "•")]) // figure placement and caption style set figure(placement: top) show figure.caption: set text(size: 0.85em) show figure.caption: set align(left) // citation style set cite( form: "prose" ) show cite: it => text(fill: background-color, it) // add space for heading show heading: it => v(0.3em) + it + v(0.3em) show heading.where(level: 1): it => it + v(0.3em) show heading.where(level: 4): it => text(style: "italic", weight: "regular", it) set math.equation(numbering: "(1)", supplement: [Eq.]) set outline(depth: 2) // // Included content // // code blocks show raw.where(block: true) : it => h(0.5em) + box(fill: background-color.lighten(80%), outset: 0.5em, width: 100%, it) + h(0.5em) let authors_block(people, denomination: "Author") = { if people.len() == 0 { return } let prefix = denomination if people.len() > 1 { prefix += "s" } stack( dir: ltr, text(prefix + ": ", weight: 600), stack( dir: ttb, spacing: 0.8em, ..people ) ) } // // Title page // // Title page background set page( // the title page should not have any margin, this will be reset in the next page margin: 0pt, ) [ #set image( width: 105%, ) // set the image first so that it is the lowest layer #place( bottom, background-image ) ] // define the base widht of a tile, as a tenth of the page width let tile_width = 1.51cm // Add a tiling of white squares over the background to simulate a grid for i in range(0, 14) { for j in range(0, 14) { place( bottom + right, dx: -i * tile_width + 0.1em, dy: -j * tile_width, )[ #square( size: tile_width, // fill: gradient.linear( // color.white, // color.black.transparentize(0%), // color.black.transparentize(0%), // color.black.transparentize(0%), // color.black.transparentize(0%), // angle: 45deg, // ), fill: none, stroke: ( paint: color.white, thickness: 0.02em, ) ) ] } } place( top + left, dx: -20em, line( angle: -10deg, length: 200%, stroke: ( paint: background-color.lighten(alpha), thickness: 900pt, ), ), ) place( top + left, dx: -20em, line( angle: -10deg, length: 200%, stroke: ( paint: background-color, thickness: 800pt, ) ), ) place( bottom + right, dx: 20em, dy: 20em, line( angle: -10deg, length: 200%, stroke: ( paint: background-color.lighten(alpha), thickness: 400pt, ) ), ) place( bottom + right, dx: 20em, dy: 20em, line( angle: -10deg, length: 200%, stroke: ( paint: background-color, thickness: 300pt, ) ), ) // add a few more tiles *above* the background image to simulate a grid structure let draw_pairs = ( (0, 10), // (1, 10), // (2, 10), (3, 10), // (4, 10), (5, 10), (6, 10), // (7, 10), (8, 10), // (9, 10), (10, 10), (11, 10), // (12, 10), (13, 10), (0, 11), (1, 11), // (2, 11), (3, 11), // (4, 11), (5, 11), // (6, 11), // (7, 11), (8, 11), // (9, 11), (10, 11), (11, 11), // (12, 11), (13, 11), (0, 12), // (1, 12), // (2, 12), (3, 12), // (4, 12), (5, 12), // (6, 12), // (7, 12), // (8, 12), (9, 12), // (10, 12), // (11, 12), // (12, 12), // (13, 12), // (0, 13), (1, 13), // (2, 13), (3, 13), // (4, 13), // (5, 13), // (6, 13), (7, 13), // (8, 13), // (9, 13), // (10, 13), // (11, 13), (12, 13), // (13, 13), // (0, 14), // (1, 14), // (2, 14), // (3, 14), (4, 14), // (5, 14), // (6, 14), // (7, 14), // (8, 14), // (9, 14), (10, 14), // (11, 14), // (12, 14), (13, 14), ) for (i, j) in draw_pairs { place( bottom + right, dx: -i * tile_width + 0.1em, dy: -j * tile_width, )[ #square( size: tile_width, fill: none, stroke: ( paint: color.white, thickness: 0.02em, ) ) ] } // Title page content pad( x: 4em, y: 4em, )[ #set text(font: font, fill: font-color) #align(center, text(title, size: 2.5em, weight: 600)) #if subtitle != none { v(2.5em, weak: true) align(center, text(subtitle, size: 1.8em, weight: 500)) } #pad( x: 6em, y: 0em, )[ #stack( dir: ltr, authors_block(authors), h(1fr), authors_block(supervisors, denomination: "Supervisor") ) ] ] let padded_logos = logos.map(logo => pad(x: 0.2cm, logo)) place(bottom + right, dy: -2em, dx: -2em)[ #set text(font: font, fill: font-color, size: 1.2em) #set image(height: 0.8cm, width: auto) #date #stack( dir: ltr, // text(font: font, 1em, affiliation, fill: font-color), ..padded_logos ) ] if pre_content != none { pagebreak() place( top + left, // dx: 2em, // dy: 2em, )[ #pre_content ] } pagebreak() let footer = grid( rows: auto, v(0mm), line(length: 100%, stroke: (paint: background-color, thickness: 1pt)), v(2.5mm), text( )[ #title #h(1fr) #context [ #text(counter(page).display()) ] // context needed for page counter for typst >= 0.11.0 ] ) set page( // no header footer: footer, margin: 4em, ) counter(page).update(1) // // "First" page - abstract and TOC // pad(x: 1.5em)[ #par(justify: true, abstract) ] v(2em) outline() pagebreak() // // Main body. // set heading(numbering: "1.") set par(justify: true) body }