/// 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) let font-color = color; // Check if the background color is closer to black or white let components = background-color.components() // show the components for debugging 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 } //customize look of figure // set figure.caption(separator: [ --- ], position: top) //customize inline raw code show raw.where(block: false) : it => h(0.5em) + box(fill: color.lighten(90%), outset: 0.2em, it) + h(0.5em) // Set body font family. set text(font: font, 12pt) show heading: set text(font: font, fill: background-color) // add space for heading show heading.where(level:1): it => it + v(0.5em) // Set link style 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) //numbered list colored set enum(indent: 1em, numbering: n => [#text(fill: background-color, numbering("1.", n))]) //unordered list colored set list(indent: 1em, marker: n => [#text(fill: background-color, "•")]) set cite( form: "prose" ) // display of outline entries show outline.entry: it => text(size: 12pt, weight: "regular",it) // // 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 the image first so that it is the lowest layer place( bottom, background-image ) // 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 * 3.55em + 0.1em, dy: -j * 3.55em, )[ #square( size: 3.55em, // 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 * 3.55em + 0.1em, dy: -j * 3.55em, )[ #square( size: 3.55em, // 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, ) ) ] } // Title page content pad( x: 4em, y: 4em, )[ #align(center, text(font: font, 3em, weight: 700, title, fill: font-color)) #v(2em, weak: true) #if subtitle != none { align(center, text(font: font, 2em, weight: 600, subtitle, fill: font-color)) } #v(2em, weak: true) #align( center, text(font: font, 1em, authors.join(", "), fill: font-color) ) ] 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, ) // // Table of contents. // abstract v(2em) outline() pagebreak() // // Main body. // set heading(numbering: "1.") set par(justify: true) body } // let footer = grid( // rows: auto, // v(0mm), // line(length: 100%, stroke: 0.6pt), // should be 1.6pt according to guidelines // v(2.5mm), // text( // font: "Roboto", // stretch: 100%, // fallback: false, // weight: "regular", // size: 10pt // )[ // #set align(right) // // context needed for page counter for typst >= 0.11.0 // #context [ // #let counter_disp = counter(page).display() // //#hide(counter_disp) // //#counter_disp // #context { // let after_table_of_contents = query(selector(<__after_table_of_contents>).before(here())).len() >= 1 // if after_table_of_contents {counter_disp} // else {hide(counter_disp)} // } // ] // ] // )