diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07fa008 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/*.pdf +flake.lock diff --git a/README.md b/README.md new file mode 100644 index 0000000..e01bf94 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# Clean Polylux Template + +This is a clean and dynamic presentation template for [Polylux](https://github.com/andreasKroepelin/polylux), a package for [Typst](https://typst.app/) to create nice looking presentations. + +Initial work was already done, but I added lots of neat features, so now this template features: +- An easy to use templating interface, which just requires some meta information +- A footer with arbitrary text and a slide counter +- A slide counter, that does not suck! (as it only counts real slides and shows a total amount) +- Dynamic logos on the title slide +- Dynamic coloring via variables +- Automatic creation of a contents slide +- Dynamic header on each slide showing the slide's name and current section +- Focus slides + +## Screenshots +| Light Theme with Green Accent | Light Theme with Orange Accent | Dark Theme with Purple Accent | +|:--:|:--:|:--:| +|![light1](./screenshots/light1.png)|![light2](./screenshots/light2.png)|![dark1](./screenshots/dark1.png)| + +![titlepage](./screenshots/titlepage.png) + +![contents](./screenshots/contents.png) + +## How to use +See [presentation.typ](./presentation.typ) for a sample presentation. +Make sure you have `typst` installed, otherwise you could use the provided Nix Flake with `nix develop .` + +To just compile the presentation, run: +```sh +$ typst compile presentation.typ --open +``` + +To have a live preview, run: +```sh +$ typst watch presentation.typ --open +``` + +## Configure +The entire templating part is done in [theme.typ](./theme.typ). +Every major variable can be found towards the top of the file, marked with `CONFIG:` comments. +Here you can configure the font and the color of the slides, the rest will be adjusted automatically. + + +## Contribution +Feel free to fork this repository and make adjustments as you wish, but I would appreciate a small notice somewhere. +If you find visual bugs or have feature ideas, feel free to upstream them to this repository. + +## Inspirations +- [matze/mtheme](https://github.com/matze/mtheme) +- [Enive](https://github.com/Enivex) +- [hargoniX](https://github.com/hargoniX/) diff --git a/figures/ferris.png b/figures/ferris.png new file mode 100644 index 0000000..d1e9f80 Binary files /dev/null and b/figures/ferris.png differ diff --git a/figures/polylux.png b/figures/polylux.png new file mode 100644 index 0000000..68bf2da Binary files /dev/null and b/figures/polylux.png differ diff --git a/figures/typst.svg b/figures/typst.svg new file mode 100644 index 0000000..478d16c --- /dev/null +++ b/figures/typst.svg @@ -0,0 +1,9 @@ + + + diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..a545f6b --- /dev/null +++ b/flake.nix @@ -0,0 +1,35 @@ +{ + description = "Nightly Typst with Typst LSP"; + + inputs.typst-lsp.url = "github:nvarner/typst-lsp"; + inputs.typst.url = "github:typst/typst"; + inputs.utils.url = "github:numtide/flake-utils"; + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + outputs = { + nixpkgs, + utils, + typst, + typst-lsp, + ... + }: + utils.lib.eachDefaultSystem (system: let + typst-overlay = _self: _super: { + typst-lsp = typst-lsp.packages.${system}.default; + typst = typst.packages.${system}.default.overrideAttrs (_old: { + dontCheck = true; + }); + }; + pkgs = nixpkgs.legacyPackages.${system}.appendOverlays [typst-overlay]; + typst-shell = pkgs.mkShell { + nativeBuildInputs = [ + pkgs.typst-lsp + pkgs.typst + ]; + }; + in { + devShells.default = typst-shell; + overlays.default = typst-overlay; + legacyPackages = pkgs; + }); +} diff --git a/presentation.typ b/presentation.typ new file mode 100644 index 0000000..cb9a7ba --- /dev/null +++ b/presentation.typ @@ -0,0 +1,41 @@ +#import "theme.typ": * + +#show: presentation.with( + author: [Sample Author \], + title: [Sample Title], + occasion: [Sample Occasion], + date: [01.01.1970], + logos: ("figures/typst.svg", "figures/polylux.png", "figures/ferris.png"), + logo_height: 40%, + footer: [This is a really cool footer], +) + +#section("My Section") +#slide(title: "My Title")[ + #grid( + columns: (50%, 50%), + [ + - #lorem(10) + - #lorem(10) + - #lorem(10) + - #lorem(10) + ], + image("figures/ferris.png") + ) +] + +#focus-slide()[ + We need to focus now... +] + +#slide(title: "Some code!")[ + #figure( + sourcecode(```rust + enum Foo { + Bar1(Box), + Bar2(String), + } + ```), + caption: [Some awesome Rust!] + ) +] diff --git a/screenshots/contents.png b/screenshots/contents.png new file mode 100644 index 0000000..d8b5111 Binary files /dev/null and b/screenshots/contents.png differ diff --git a/screenshots/dark1.png b/screenshots/dark1.png new file mode 100644 index 0000000..7913339 Binary files /dev/null and b/screenshots/dark1.png differ diff --git a/screenshots/light1.png b/screenshots/light1.png new file mode 100644 index 0000000..6d3bc83 Binary files /dev/null and b/screenshots/light1.png differ diff --git a/screenshots/light2.png b/screenshots/light2.png new file mode 100644 index 0000000..f460052 Binary files /dev/null and b/screenshots/light2.png differ diff --git a/screenshots/titlepage.png b/screenshots/titlepage.png new file mode 100644 index 0000000..a1b608c Binary files /dev/null and b/screenshots/titlepage.png differ diff --git a/theme.typ b/theme.typ new file mode 100644 index 0000000..5eef6d9 --- /dev/null +++ b/theme.typ @@ -0,0 +1,191 @@ +#import "@preview/polylux:0.3.1": * +#import "@preview/codelst:1.0.0": sourcecode + +// CONFIG: Font +#let font = "Roboto" +#let weight = "light" +#let size = 20pt + +// CONFIG: Color +#let color-primary = rgb("#66A182") +#let color-foreground = rgb("#5c6a72") +#let color-background = rgb("#ffffff") + +#let footer-lighten-value = 50% // how much lighter the color of the footer is +#let section-foreground = color-background // color of text on section slide and headers +#let focus-background = color-foreground // background of the focus slide +#let focus-foreground = color-background // foreground of focus slide + +// Footer in the bottom left +#let custom-footer = state("custom-footer", none) + +// Last section in the top left +#let last-section = state("last-section", none) + +// A normal slide +#let slide(title: none, is_toc: false, body) = { + let header-cell = block.with( + width: 100%, + height: 100%, + above: 0pt, + below: 0pt, + breakable: false + ) + + // Bar in the top + let header = { + set align(top) + if title != none { + show: header-cell.with(fill: color-primary, inset: 1.2em) + set align(horizon) + + if not is_toc { + text(fill: section-foreground, size: 0.6em, last-section.display()) + text([\ ]) + } + text(fill: section-foreground, size: 1.2em, strong(title)) + + } else { [] } + } + + // Footer with text on the left and slide count on the right + let footer = { + set text(size: 0.6em) + show: pad.with(1em) + set align(bottom) + let footer-color = color-foreground.lighten(footer-lighten-value) + text(fill: footer-color, custom-footer.display()) + h(1fr) + text(fill: footer-color, [#logic.logical-slide.display()/#utils.last-slide-number]) + } + + set page( + header: header, + footer: if not is_toc { footer } else [], + margin: (top: 5em), + fill: color-background, + ) + + let content = { + show: align.with(horizon) + show: pad.with(left: 2em, right: 2em, top: -1.5em) + set text(fill: color-foreground) + body + } + + logic.polylux-slide(content) + + if is_toc { + // Don't count TOC slide towards slide count + logic.logical-slide.update(0) + } +} + + +// The actual presentation main +#let presentation( + aspect-ratio: "16-9", + title: [Sample title], + occasion: [Sample occasion], + author: [Sample Author], + date: [01.01.1970], + logos: (), + logo_height: 50%, + footer: [], + body +) = { + set text(font: font, weight: weight, size: size) + set strong(delta: 100) + set par(justify: true) + + set page( + paper: "presentation-" + aspect-ratio, + fill: color-background, + margin: 0em, + header: none, + footer: none, + ) + + // save foother to global state + custom-footer.update(footer) + + let title-slide = { + set text(fill: color-foreground, size) + set align(center + horizon) + + block(width: 100%, inset: 2em, { + // Logo + if type(logos) == type("string") { // fix buggy behavior, with a single entry + align(center+horizon, image(logos, height: logo_height)) + } else if logos.len() == 0 { + // Do not show any logos + } else { + grid( + columns: logos.len(), + ..logos.map((logo) => (align(center+horizon, image(logo, width: logo_height)))) + ) + } + + text(size: 1.3em, strong(title)) + + line(length: 100%, stroke: .05em + color-primary) + + set text(size: .8em) + h(1fr) + if author != none { + block(spacing: 1em, author) + } + if date != none { + block(spacing: 1em, date) + } + set text(size: .8em) + + h(1fr) + if occasion != none { + set text(weight: "regular") + block(spacing: 1em, occasion) + } + + }) + } + + logic.polylux-slide(title-slide) + + // Show TOC + slide(title: "Contents", is_toc: true)[ + #utils.polylux-outline(enum-args: (tight: false,)) + ] + + body +} + + +// Section brake slides with big title printed +#let section(name) = { + set page(fill: color-primary) + let content = { + utils.register-section(name) + set align(horizon + center) + show: pad.with(20%) + set text(size: 2em, weight: "bold", fill: section-foreground) + name + } + + logic.polylux-slide(content) + + // Do not count section slides towards total slide count + logic.logical-slide.update(i => i - 1) + + // Update last section name to display it in the following slides + last-section.update(name) +} + +// Only show the text centered and big (good for a final slide) +#let focus-slide(body) = { + set page(fill: focus-background, margin: 2em) + set text(fill: focus-foreground, size: 1.5em) + logic.polylux-slide(align(horizon + center, body)) + + // Do not count focus slides towards total slide count + logic.logical-slide.update(i => i - 1) +}