28 Commits

Author SHA1 Message Date
1cc464fa9b workflow: i guess upload-actions v4 is GH only 2025-09-08 23:48:30 -07:00
4c4e79e384 gitea workflow: try non ubuntu chrome 2025-09-08 23:39:12 -07:00
ae57353e8f workflow: prefer runner os 2025-09-08 23:33:18 -07:00
fdb4840912 fix workflow 2025-09-08 23:29:59 -07:00
6f4ae177da gitea: build pdf on push 2025-09-08 23:25:33 -07:00
9b9648d033 new page: Crash Man 2025-09-08 23:22:25 -07:00
4ffc138909 Merge pull request 'Some interlude pages' (#7) from zanneth/smartbar:zanneth/interludes into master
Reviewed-on: buzzert/smartbar#7
2025-09-09 04:48:52 +00:00
Charles Magahern
08354c8905 Some interlude pages 2025-09-07 18:39:15 -07:00
dc8ab1a0a7 Merge pull request 'fix embarrassing typo' (#6) from phajas/smartbar:phajas/vrankle_typo into master
Reviewed-on: buzzert/smartbar#6
2025-09-02 00:44:38 +00:00
Peter Hajas
4e19b2a1b0 fix embarrassing typo 2025-09-01 14:12:17 -06:00
803f07a1b7 Merge branch 'phajas-phajas/vrankle'
* phajas-phajas/vrankle:
  Add "Vrankle: A Victim"
2025-08-31 16:48:46 -06:00
fd17e511dc Adds pdfbook command 2025-08-31 16:47:15 -06:00
29030c573c Adds new pdfbook command (ai generated) 2025-08-31 16:46:54 -06:00
3fc555844b Revert "fix namespacing with CSS"
This reverts commit 896a67ca8a.
2025-08-31 16:46:54 -06:00
182ebd14b3 Merge pull request '[New Article] Pearl Street Café' (#5) from zanneth/smartbar:zanneth/pearl-street-cafe into master
Reviewed-on: buzzert/smartbar#5
2025-08-31 22:44:16 +00:00
da10b32dfa adds wtf page and interstitial hack the planet 2025-08-31 16:07:07 -06:00
Charles Magahern
d31123f24c pearlstreetcafe: Formatting tweaks 2025-08-31 15:05:56 -07:00
Charles Magahern
aac3a7b302 pearlstreetcafe: Beavis -> Trevor 2025-08-31 14:50:52 -07:00
Charles Magahern
cb93752ce7 pearlstreetcafe: Formatting tweaks 2025-08-31 13:50:52 -07:00
Charles Magahern
35a7d49a56 pearlstreetcafe: First draft 2025-08-31 13:44:56 -07:00
Peter Hajas
4aa310c008 Add "Vrankle: A Victim" 2025-08-31 14:44:54 -06:00
8c3232c788 Adds cover page 2025-08-31 14:02:46 -06:00
f3eed964f0 Merge pull request 'fix namespacing with CSS' (#3) from phajas/smartbar:phajas/fix_namespacing into master
Reviewed-on: buzzert/smartbar#3
2025-08-31 18:53:23 +00:00
Peter Hajas
896a67ca8a fix namespacing with CSS 2025-08-31 12:46:36 -06:00
0fb28a1df8 Merge pull request '[New Article] Linux GTK Apps: A Language Comparison' (#1) from zanneth/smartbar:zanneth/gtkapplang into master
Reviewed-on: buzzert/smartbar#1
2025-08-31 18:36:34 +00:00
Charles Magahern
7b55b65a34 gtkapplang: Increase margin 2025-08-31 11:22:37 -07:00
Charles Magahern
cef6b7597b gtkapplang: Pages 3 and 4, final edits
Conflicts:
	pages.yaml
2025-08-31 11:22:34 -07:00
Charles Magahern
1afb4d5fa6 gtkapplang: First two pages
Conflicts:
	pages.yaml
2025-08-31 11:22:12 -07:00
46 changed files with 1096 additions and 32 deletions

View File

@@ -0,0 +1,55 @@
name: Build PDF
on:
push:
branches: [ main, master ]
pull_request:
workflow_dispatch:
jobs:
pdf:
name: make pdf
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go (from go.mod)
uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
cache: true
- name: Install Chrome and tools (no snap)
run: |
set -euxo pipefail
if command -v sudo >/dev/null 2>&1; then SUDO=sudo; else SUDO=; fi
$SUDO apt-get update
# Base tools and fonts
DEBIAN_FRONTEND=noninteractive $SUDO apt-get install -y --no-install-recommends \
ca-certificates gnupg make fonts-liberation
# Add Google's official Chrome APT repo (avoids Ubuntu's snap-only chromium)
$SUDO install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | $SUDO gpg --dearmor -o /etc/apt/keyrings/google-linux-signing-keyring.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-linux-signing-keyring.gpg] https://dl.google.com/linux/chrome/deb/ stable main" | \
$SUDO tee /etc/apt/sources.list.d/google-chrome.list >/dev/null
$SUDO apt-get update
DEBIAN_FRONTEND=noninteractive $SUDO apt-get install -y --no-install-recommends google-chrome-stable
# Clean up apt lists to keep image lean
$SUDO rm -rf /var/lib/apt/lists/*
- name: Build PDF
env:
# Ensure our tools pick Chrome first if multiple are present
CHROME_PATH: /usr/bin/google-chrome-stable
run: make pdf
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: output-pdf
path: _dist/output.pdf
if-no-files-found: error

View File

@@ -34,8 +34,8 @@ deps:
.PHONY: pdf .PHONY: pdf
pdf: build deps pdf: build deps
@echo "Generating PDF with headless Chrome..." @echo "Generating PDF by rendering each page and merging..."
@$(GO) run ./cmd/pdf --in $(OUT_DIR)/print.html --out $(PDF) @$(GO) run ./cmd/pdfbook --pages $(OUT_DIR) --order pages.yaml --out $(PDF)
.PHONY: pdf-2up .PHONY: pdf-2up
pdf-2up: build deps pdf-2up: build deps

View File

@@ -3,6 +3,11 @@
margin: 0; margin: 0;
} }
@font-face {
font-family: "Heading Now";
src: url("../font/HeadingNow-95Medium.otf");
}
/* Base */ /* Base */
html, body { html, body {
height: 100%; height: 100%;
@@ -20,6 +25,39 @@ html, body {
margin: 0; margin: 0;
} }
.interlude {
position: relative;
overflow: hidden;
color: white;
padding: 4rem;
z-index: 0; /* ensure stacking context */
}
.interlude::before {
content: '';
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
filter: grayscale(20%) brightness(0.7);
z-index: -2;
}
.interlude::after {
content: '';
position: absolute;
inset: 0;
/*background: rgba(128, 0, 128, 0.3); /* purple tint */ */
z-index: -1;
}
.interlude-caption {
color: rgba(225, 225, 225, 1.0);
font-family: "Heading Now";
line-height: 2em;
rotate: 5deg;
}
/* Use border-box sizing everywhere to keep dimensions predictable */ /* Use border-box sizing everywhere to keep dimensions predictable */
html { box-sizing: border-box; } html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; } *, *::before, *::after { box-sizing: inherit; }

60
assets/css/gtkapplang.css Normal file
View File

@@ -0,0 +1,60 @@
#article-header {
background-color: #0d0d0d;
color: white;
text-align: center;
padding: 5pt 0 5pt 0;
}
#article-header > div {
display: inline-block;
vertical-align: middle;
margin: 5pt;
}
img#gtk-logo {
width: 50px;
}
#article-body {
columns: 2;
font: 8.75pt/1.40 Helvetica, sans-serif;
padding: 0.25in 0.5in 0 0.5in;
}
#article-body p {
text-indent: 2em;
}
#article-body p:first-child {
margin-top: 0;
text-indent: 0;
}
/*
span.first-word {
font-size: xx-large;
font-weight: bold;
line-height: 0;
}
*/
h1, h2, h3 {
margin: 2pt;
text-align: left;
font-family: Helvetica, sans-serif;
}
h3 {
font-weight: normal;
font-style: italic;
}
.code {
font-family: monospace;
}
.endmark {
text-align: center;
margin-top: 1em;
color: #555;
}

View File

@@ -1,3 +1,17 @@
#page { @font-face {
font-family: "Heading Now";
src: url("../font/HeadingNow-95Medium.otf");
}
.ytmnd-1 {
color: #fff;
text-shadow:
0px 10px 0 rgb(209, 1, 209),
0px 20px 0 black;
}
.ytmnd-2 {
color: #fff;
text-shadow:
0px 6px 0 rgb(209, 1, 209);
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

BIN
assets/img/coffee-shop.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
assets/img/cover.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

BIN
assets/img/free-dirt.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
assets/img/javaguy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

BIN
assets/img/logo-gtk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
assets/img/mr-nickel.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
assets/img/vrankle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
assets/img/wtf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@@ -160,21 +160,12 @@ func main() {
panic(err) panic(err)
} }
// Build print.html containing all pages one after another for PDF // Prepare pages for aggregate printing (2-up, etc.)
printTplPath := filepath.Join(templatesDir, "print.gohtml")
printTpl := template.Must(template.New("print").Parse(mustReadFile(printTplPath)))
var printPages []PrintPage var printPages []PrintPage
for _, pagePath := range pageFiles { for _, pagePath := range pageFiles {
content := strings.TrimSpace(mustReadFile(pagePath)) content := strings.TrimSpace(mustReadFile(pagePath))
printPages = append(printPages, PrintPage{Content: template.HTML(content)}) printPages = append(printPages, PrintPage{Content: template.HTML(content)})
} }
var outPrint bytes.Buffer
if err := printTpl.Execute(&outPrint, map[string]any{"Pages": printPages, "PageSizeAttr": pageSizeStyleAttr(config.PageWidthIn, config.PageHeightIn)}); err != nil {
panic(err)
}
if err := writeFile(filepath.Join(outDir, "print.html"), outPrint.Bytes()); err != nil {
panic(err)
}
// Build 2-up print HTML (letter pages with two half-letter pages side by side) // Build 2-up print HTML (letter pages with two half-letter pages side by side)
var sheets []TwoUpSheet var sheets []TwoUpSheet

158
cmd/pdfbook/main.go Normal file
View File

@@ -0,0 +1,158 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"smartbar/internal/config"
"github.com/chromedp/cdproto/emulation"
"github.com/chromedp/cdproto/page"
"github.com/chromedp/chromedp"
pdfapi "github.com/pdfcpu/pdfcpu/pkg/api"
)
func must(err error) {
if err != nil {
log.Fatal(err)
}
}
func parseYAMLListOfStrings(src string) ([]string, error) {
var out []string
for _, line := range strings.Split(src, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(line, "-") {
item := strings.TrimSpace(strings.TrimPrefix(line, "-"))
if item != "" {
out = append(out, item)
}
} else {
return nil, fmt.Errorf("invalid yaml line (expect '- item'): %s", line)
}
}
return out, nil
}
func findChromeExec() (string, error) {
if v := os.Getenv("CHROME_PATH"); v != "" {
return v, nil
}
candidates := []string{"google-chrome-stable", "google-chrome", "chromium-browser", "chromium", "chrome"}
for _, name := range candidates {
if p, err := exec.LookPath(name); err == nil {
return p, nil
}
}
// macOS common app bundle paths
macPaths := []string{
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
"/Applications/Chromium.app/Contents/MacOS/Chromium",
}
for _, p := range macPaths {
if _, err := os.Stat(p); err == nil {
return p, nil
}
}
return "", fmt.Errorf("no Chrome/Chromium executable found; set CHROME_PATH or install chromium/google-chrome")
}
func main() {
var (
pagesDir string
orderPath string
outPDF string
width float64
height float64
)
flag.StringVar(&pagesDir, "pages", "_dist", "path to compiled pages directory (required)")
flag.StringVar(&orderPath, "order", "pages.yaml", "path to YAML file listing page order (required)")
flag.StringVar(&outPDF, "out", "_dist/output.pdf", "output PDF path (required)")
flag.Float64Var(&width, "w", config.PageWidthIn, "page width in inches")
flag.Float64Var(&height, "h", config.PageHeightIn, "page height in inches")
flag.Parse()
if pagesDir == "" || orderPath == "" || outPDF == "" {
log.Fatal("--pages, --order and --out are required")
}
// Load order
data, err := os.ReadFile(orderPath)
must(err)
ordered, err := parseYAMLListOfStrings(string(data))
must(err)
// Resolve Chrome
chromeExec, err := findChromeExec()
must(err)
opts := append(chromedp.DefaultExecAllocatorOptions[:], chromedp.ExecPath(chromeExec), chromedp.Flag("headless", true), chromedp.Flag("disable-gpu", true), chromedp.Flag("hide-scrollbars", true))
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancel()
ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
// Temp dir for individual PDFs
tmpDir, err := os.MkdirTemp("", "smartbar_pdfs_*")
must(err)
defer os.RemoveAll(tmpDir)
var partPDFs []string
for _, rel := range ordered {
abs := filepath.Join(pagesDir, rel)
if _, err := os.Stat(abs); err != nil {
log.Printf("skip missing page: %s", rel)
continue
}
absHTML, _ := filepath.Abs(abs)
url := "file://" + absHTML
outPath := filepath.Join(tmpDir, strings.TrimSuffix(filepath.Base(rel), filepath.Ext(rel)) + ".pdf")
// Render one page PDF via printToPDF
err := chromedp.Run(ctx, chromedp.Tasks{
chromedp.Navigate(url),
chromedp.ActionFunc(func(ctx context.Context) error {
if err := emulation.SetEmulatedMedia().WithMedia("print").Do(ctx); err != nil {
return err
}
params := page.PrintToPDF().
WithPaperWidth(width).
WithPaperHeight(height).
WithMarginTop(0).WithMarginBottom(0).WithMarginLeft(0).WithMarginRight(0).
WithPrintBackground(true)
buf, _, err := params.Do(ctx)
if err != nil {
return err
}
return os.WriteFile(outPath, buf, 0o644)
}),
})
must(err)
partPDFs = append(partPDFs, outPath)
}
if len(partPDFs) == 0 {
log.Fatal("no pages produced; check order file")
}
// Merge PDFs into single output
must(os.MkdirAll(filepath.Dir(outPDF), 0o755))
// pdfcpu expects []string in order and writes outPDF
must(pdfapi.MergeCreateFile(partPDFs, outPDF, false, nil))
fmt.Printf("Wrote %s (%d pages)\n", outPDF, len(partPDFs))
}

17
go.mod
View File

@@ -1,11 +1,14 @@
module smartbar module smartbar
go 1.21 go 1.23.0
toolchain go1.24.2
require ( require (
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732 github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732
github.com/chromedp/chromedp v0.9.5 github.com/chromedp/chromedp v0.9.5
github.com/fsnotify/fsnotify v1.9.0 github.com/fsnotify/fsnotify v1.9.0
github.com/pdfcpu/pdfcpu v0.11.0
) )
require ( require (
@@ -13,7 +16,17 @@ require (
github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.3.2 // indirect github.com/gobwas/ws v1.3.2 // indirect
github.com/hhrutter/lzw v1.0.0 // indirect
github.com/hhrutter/pkcs7 v0.2.0 // indirect
github.com/hhrutter/tiff v1.0.2 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
golang.org/x/sys v0.16.0 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/image v0.27.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
) )

28
go.sum
View File

@@ -12,14 +12,40 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q= github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=
github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo=
github.com/hhrutter/pkcs7 v0.2.0 h1:i4HN2XMbGQpZRnKBLsUwO3dSckzgX142TNqY/KfXg+I=
github.com/hhrutter/pkcs7 v0.2.0/go.mod h1:aEzKz0+ZAlz7YaEMY47jDHL14hVWD6iXt0AgqgAvWgE=
github.com/hhrutter/tiff v1.0.2 h1:7H3FQQpKu/i5WaSChoD1nnJbGx4MxU5TlNqqpxw55z8=
github.com/hhrutter/tiff v1.0.2/go.mod h1:pcOeuK5loFUE7Y/WnzGw20YxUdnqjY1P0Jlcieb/cCw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pdfcpu/pdfcpu v0.11.0 h1:mL18Y3hSHzSezmnrzA21TqlayBOXuAx7BUzzZyroLGM=
github.com/pdfcpu/pdfcpu v0.11.0/go.mod h1:F1ca4GIVFdPtmgvIdvXAycAm88noyNxZwzr9CpTy+Mw=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@@ -1,6 +1,19 @@
# locations in pages/ # locations in pages/
- cover.html - cover.html
- bouba.html - hack-the-planet.html
- wtf.html
- drivingmissmuni.html - drivingmissmuni.html
- crashman.html
- vranklevictim.html
- pearlstreetcafe.html
- gtkapplang-1.html
- gtkapplang-2.html
- gtkapplang-3.html
- gtkapplang-4.html
- broken-screen.html
- windows-shirt.html
- free-dirt.html
- mr-nickel.html
- javaguy.html

View File

@@ -1,9 +0,0 @@
<style>
#bouba-page {
background-color: red;
}
</style>
<div id="bouba-page" class="page-base">
This is a test kiki.
</div>

19
pages/broken-screen.html Normal file
View File

@@ -0,0 +1,19 @@
<style>
#broken-screen::before {
background-image: url("assets/img/broken-screen.jpg");
}
#broken-screen-caption {
position: absolute;
bottom: 25%;
right: 15%;
text-align: right;
}
</style>
<div id="broken-screen" class="page-base interlude">
<div class="interlude-caption" id="broken-screen-caption">
have you tried<br/>
turning it off and on again?
</div>
</div>

View File

@@ -1,9 +1,49 @@
<style> <style>
#cover-page { #cover-page {
background-color: green; position: relative;
background:
linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.35)),
url("assets/img/cover.jpg") no-repeat center center;
background-size: cover;
}
#logo {
position: absolute;
top: 29px;
left: 8px;
transform: scaleY(1.85);
font-family: "Heading Now";
font-size: 68px;
color: #fff;
text-shadow:
0px 10px 0 rgb(209, 1, 209),
0px 20px 0 black;
}
#edition {
position: absolute;
right: 8px;
bottom: 20px;
transform: scaleY(0.8);
font-family: "Heading Now";
font-size: 32px;
text-align: right;
} }
</style> </style>
<div id="cover-page" class="page-base"> <div id="cover-page" class="page-base">
welcome to smart bar
<div id="logo" class="ytmnd-1">smart bar</div>
<div id="edition" class="ytmnd-2">
<div>sf cyberculture zine</div>
<div>issue 1, 2025</div>
</div>
</div> </div>

89
pages/crashman.html Normal file
View File

@@ -0,0 +1,89 @@
<style>
#crashman-page {
position: relative;
background: url("assets/img/crashman/crashman-bg.png") no-repeat center center;
background-size: cover;
overflow: hidden;
}
#snips {
height: 120px;
width: 620px;
background-color: rgb(209, 1, 209);
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 5px;
position: absolute;
bottom: 30px;
left: -50px;
transform: rotate(-5deg);
}
#snips img {
flex: 1 1 0;
width: auto;
object-fit: contain;
max-height: 100%;
}
#title {
position: absolute;
top: 350px;
right: 8px;
font-family: "Heading Now";
font-size: 48px;
color: #fff;
text-shadow:
0px 10px 0 rgb(209, 1, 209),
0px 20px 0 black;
}
#text {
position: absolute;
top: 390px;
width: 100%;
height: auto;
background-color: rgba(62, 6, 62, 0.704);
font: 11.75pt/1.40 Tahoma, sans-serif;
padding: 24px;
color: #fff;
line-height: 1.9em;
}
.stretch {
font-weight: 800;
transform: scaleX(2.1);
transform-origin: left;
display: inline-block;
}
</style>
<div id="crashman-page" class="page-base">
<div id="text">
<p>
<b>Rusty Haight</b> is the human crash-test dummy. As director of the San Diego-based Collision Safety Institute,
Rusty has experienced more than 950 violent vehicle crash tests at speeds of up to 54 mph,
and taken <span class="stretch">140 air bags to the face.</span>
</p>
<p>
Haight has contributed several publications on the topic of automobile safety
in <b>Collision Magazine</b> such as <em>Hyundai and Kia Crash Data: the Indispensable Compendium.</em>
</p>
</div>
<div id="snips">
<img src="assets/img/crashman/crashman-1.png" />
<img src="assets/img/crashman/crashman-2.png" />
<img src="assets/img/crashman/crashman-3.png" />
</div>
<div id="title" class="ytmnd-1">crash man</div>
</div>

15
pages/free-dirt.html Normal file
View File

@@ -0,0 +1,15 @@
<style>
#free-dirt::before {
background-image: url("assets/img/free-dirt.jpg");
}
#free-dirt-caption {
position: absolute;
bottom: 25%;
right: 25%;
}
</style>
<div id="free-dirt" class="page-base interlude">
<div class="interlude-caption" id="free-dirt-caption">thanks...</div>
</div>

35
pages/gtkapplang-1.html Normal file
View File

@@ -0,0 +1,35 @@
<link rel="stylesheet" href="assets/css/gtkapplang.css" />
<div id="body" class="page-base">
<div id="article-header">
<div>
<img id="gtk-logo" src="assets/img/logo-gtk.png" />
</div>
<div>
<h1>Linux GTK Apps</h1>
<h2>A Language Comparison</h2>
<h3>by P. Michael Cho</h3>
</div>
</div>
<div id="article-body">
<p><span class="first-word">A</span> famous hacker once said, "Linux is only free if your time has no value." Well, anyone who knows me knows that my time is about as worthless as a bag of salt. Thus, I really had nothing to lose when I decided to ditch the glitzy, glamorous commercial OS'es and go all-in on Linux this year.</p>
<p>It's really not that bad in 2025. Maybe it really is the <em>Year of the Linux Desktop.</em> A lot of stuff just works out of the box. You don't have to waste a whole weekend getting sound to work in YouTube videos. X11 isn't really a thing anymore, so fiddling with ancient hieroglyphic <strong>Xorg config files</strong> is no longer required just to get your anime girlfriend wallpaper desktop to show up on the screen.</p>
<p>Emboldened with a new sense of optimism for the Linux desktop, I decided to try writing a few <strong>native apps</strong> to see what the developer ecosystem is like. <strong>GTK,</strong> formerly known as the <strong>GIMP ToolKit,</strong> appears to be the de-facto widget toolkit for creating native GUI apps on Linux, so I decided to learn and explore how to develop apps using this toolkit.</p>
<p>The first problem I encountered was a severe sense of language paralysis. There are many, many choices of programming languages when developing GTK apps, which isn't necessarily a good thing. Apple doesn't always do everything right, but upholding Swift as the one true language for developing macOS and iOS apps reduces a lot of fragmentation for developers. A walled garden keeps the snakes out! Nevertheless, I decided to try and write a few small apps in a variety of different languages to see which one feels the best.</p>
<h4>C (Rating: B-)</h4>
<p>The first language I decided to try is an oldie-but-goodie. Just plain ol' C. Linux guys really like C, and really hate pretty much every other language, and the GTK toolkit itself is implemented in C, so it seemed like a good first choice.</p>
<p>One cool thing about GTK is that it is based on runtime library called <strong>GLib,</strong> which implements a bunch of object-oriented design patterns in C. It defines an object model, lifetime semantics, and a bunch of commonly used data structures that you can use in your app. Object-oriented programming is definitely out of style nowadays but I still think it is a solid paradigm for developing user interfaces. It makes sense conceptually for all widgets to inherit from a base class that defines things like screen geometry and parent-child relationships.</p>
<p>My first couple of hours with C were glorious. The code was just falling out of my hands. The language is so simple, it's nearly impossible to waste any time on design.</p>
</div>
</div>

25
pages/gtkapplang-2.html Normal file
View File

@@ -0,0 +1,25 @@
<link rel="stylesheet" href="assets/css/gtkapplang.css" />
<div id="body" class="page-base">
<div id="article-body">
<p>Unfortunately this honeymoon phase quickly came to an end. Writing in C requires a <strong>lot</strong> of boilerplate code, and significant use of <strong>macros,</strong> against which I am generally ideologically opposed. For every "class" type that you define in your app, you have to implement several functions: one that initializes the class itself with the runtime, another for handling dynamic setting of properties, another to handle getting those properties, one for initializing instances of that class, and one for destroying or "finalizing" instances of that class. Confusingly there are two different functions for destroying objects, one called <strong>finalize</strong> and another called <strong>dispose,</strong> and I guess this was done to eliminate cycles in the reference counting mechanism implemented by GObject. Big yuck.</p>
<p>I eventually did finish the app I was writing in pure C, but it was a shitload of code. There are probably a couple of memory leaks too. One great thing about writing in C is that compiling the code is basically instantaneous. I actually started to feel angry and upset thinking about how much time we waste compiling code in newer languages. Do people know that complex C programs take only a few seconds to compile? Debugging was excellent too. GDB just works and I can see everything. Overall a pretty good experience I would say.</p>
<h4>C++ (Rating: C-)</h4>
<p>Linux guys are going to hate me for even mentioning C++, but I of course had to give it a try. Despite its numerous flaws, it's still the best bang for your buck when it comes to offering reasonably good object-oriented features with very fast performance.</p>
<p>For using GLib and GTK in C++ I decided to use the <strong>gtkmm</strong> library, which implements a variety of C++ bindings for all of the classes in the toolkit. Along with that I also used <strong>glibmm</strong> and <strong>giomm</strong> which are required to use the GLib and GIO dependencies in C++ code.</p>
<p>I was immediately disappointed to learn that a ton of boilerplate was still required just for defining some basic data types with properties. Properties are an important abstraction in GObject that let you bind data to various widgets, so this isn't something that can be easily avoided. Basically for each data member in your class, you need <strong>three accessor functions</strong>: two that 'get' and 'set' the data itself, and a third that returns a reference to the <span class="code">Glib::Property</span> object representing that member.</p>
<p>Another disappointment was the documentation for gtkmm and glibmm. Just atrocious. Looks like autogenerated Doxygen slop. Come on guys, it's not 2008 anymore.</p>
<p>One positive thing I will say about using C++ is that memory management is a lot more tolerable than C. Using the <span class="code">Glib::RefPtr</span> smart pointer class instead of manually managing GObject lifecycles is a blessing.</p>
<p>I did end up finishing this project as well and the end result was satisfactory. Overall I didn't really have a lot of fun writing C++, but then again, who does.</p>
</div>
</div>

29
pages/gtkapplang-3.html Normal file
View File

@@ -0,0 +1,29 @@
<link rel="stylesheet" href="assets/css/gtkapplang.css" />
<div id="body" class="page-base">
<div id="article-body">
<h4>Rust (Rating: C+)</h4>
<p>Of course I cannot go without mentioning the fad language of the decade: <strong>Rust.</strong> I was actually pretty excited to try using Rust to write a GTK app. The users and designers of the language alike are very opinionated, Rust programs are usually very high quality, and I generally like the overall design of the language and the standard library.</p>
<p>I decided to use the <strong>gtkrs</strong> crate for my Rust app, which was trivial to setup using Rust's excellent build system. From what I can tell, a majority of the code in gtkrs is automatically generated, so there isn't a lot of manual wrapping done, which is quite nice.</p>
<p>First impressions were very positive. Just like with C++, memory management was straightforward and conventional. I didn't waste a lot of time allocating objects and passing them around the library. The designers of gtkrs did a really good job in creating the bindings while also making them memory safe, which is very much concordant with the Rust philosophy.</p>
<p>Significantly less boilerplate was required with Rust as well, thanks to macros provided by the <span class="code">glib</span> crate like <span class="code">property</span> and <span class="code">object_subclass</span>. Getter and setter functions are optional and mostly for convenience.</p>
<p>So why did I give Rust a <strong>C+</strong> rating? The main reason is because Rust is not an object-oriented language. It feels really unnatural to shoehorn object-oriented design patterns into Rust when it was clearly designed not to support that. Of course, it <em>kinda works,</em> and you can finish your app with some pretty nice code to maintain, but it feels sort of like writing classical poetry in LaTeX.</p>
<p>I finished writing my app in Rust and felt pretty good about it, but to be honest I did have to rewrite large parts of it multiple times. It took me a few tries to figure out how to make the compiler happy while still keeping things "Rusty" if you'll pardon the phrase.</p>
<h4>Vala (Rating: B)</h4>
<p>The last language I tried was one I've never heard of before, and probably you haven't either. <strong>Vala</strong> is an object-oriented language that is built by the GNOME team and sits on top of the GLib runtime. It has the standard set of nice-to-have features in modern languages such as async/await, generics, and type inference. It also has a few features that are specifically designed to interact nicely with the GObject runtime, such as signals and properties.</p>
<p>Another cool thing about Vala is that all of your code gets cross-compiled to decently human-readable C code. So there isn't a lot of worry about cross-platform support, debuggability, or performance, at least compared to C.</p>
<p>Vala has by far the least amount of boilerplate of all the languages I tried. Because it natively supports properties, your data classes and widget subclasses are extremely minimal and easy to understand.</p>
</div>
</div>

17
pages/gtkapplang-4.html Normal file
View File

@@ -0,0 +1,17 @@
<link rel="stylesheet" href="assets/css/gtkapplang.css" />
<div id="body" class="page-base">
<div id="article-body">
<p>Documentation for Vala and its libraries is pretty good too. All of it is hosted at the <strong>Valadoc</strong> website which is really easy to search and has a nice layout. I can tell that quite a lot of it is autogenerated from the C documentation, so sometimes it's a bit awkward to read the parts written in prose, but overall not bad.</p>
<p>It's quite easy to create Vala bindings to C libraries, especially if those C libraries are also implemented using the GLib runtime and use GObjects. You create these little files called <strong>VAPI</strong> files and the compiler does most of the work for you. By far one of the easiest FFI's I've ever used.</p>
<p>I think the only thing I really don't like about Vala is that the language itself doesn't really <em>spark joy.</em> For language connoisseurs like myself, it just feels kind of <em>meh.</em> From what I can tell that was actually one of the goals of the language. The designers didn't really set out to push the boundaries of programming language theory with Vala, and instead just wanted a simple language that makes it easier to write apps. I respect that.</p>
<p>Another weird and perhaps surprising disappointment is that every LLM I tried is very poor at generating Vala code. There is actually quite a lot of Vala code out on the Internet, and so statistically speaking it seems likely that it would be part of some training data. My theory is that because a lot of these Linux guys still have a beef with Microsoft, they refuse to upload a lot of code to Github which is the primary website from where code gets scraped. If someone trains an LLM with all the code on these little Gitlab instances, I'm excited to try it.</p>
<p>Overall I found Vala very pleasant to use. I think if the community hivemind were to select a language as its primary language for GTK development, it would be Vala. Just make sure to brush up on your hands-on-the-keyboard coding skills, since you won't be able to <em>vibe-code</em> your way out of some problems with Vala. <span class="endmark"></span></p>
</div>
</div>

View File

@@ -0,0 +1,10 @@
<style>
#hack-the-planet-page {
background: url("assets/img/hack-the-planet.jpg") no-repeat center center;
background-size: cover;
}
</style>
<div id="hack-the-planet-page" class="page-base">
</div>

18
pages/javaguy.html Normal file
View File

@@ -0,0 +1,18 @@
<style>
#javaguy::before {
background-image: url("assets/img/javaguy.jpg");
}
#javaguy-caption {
position: absolute;
top: 5%;
left: 5%;
}
</style>
<div id="javaguy" class="page-base interlude">
<div class="interlude-caption" id="javaguy-caption">
i'm living in the real world<br/>
and he's living in a <em>virtual machine...</em><br/>
</div>
</div>

19
pages/mr-nickel.html Normal file
View File

@@ -0,0 +1,19 @@
<style>
#mr-nickel::before {
background-image: url("assets/img/mr-nickel.jpg");
}
#mr-nickel-caption {
position: absolute;
bottom: 10%;
right: 5%;
}
</style>
<div id="mr-nickel" class="page-base interlude">
<div class="interlude-caption" id="mr-nickel-caption">
mr. nickel says<br/>
the financial collapse is imminent<br/>
and it's all your fault!
</div>
</div>

View File

@@ -0,0 +1,85 @@
<style>
@font-face {
font-family: "Chalk";
src: url("assets/font/Chalk-Regular.ttf");
}
div#body {
columns: 3;
font: 8.4pt 'Times New Roman', serif;
padding: 0.5in;
background-image: url("assets/img/coffee-shop.jpg");
background-color: rgba(0, 0, 0, 0.5);
background-blend-mode: overlay;
background-repeat: no-repeat;
background-size: cover;
background-position-x: -250px;
}
.drop-cap::first-letter {
float: left;
font-size: 4em;
line-height: 0.8;
padding-right: 8px;
padding-top: 0px;
font-weight: bold;
font-family: Georgia, serif;
}
.drop-cap {
text-indent: 0 !important;
}
h1, h2 {
color: white;
}
h1 {
font-family: Chalk, Helvetica, sans-serif;
font-size: xx-large;
margin-bottom: 0;
}
h2 {
font-size: small;
font-style: italic;
font-weight: normal;
color: lightgray;
}
#body p {
color: white;
text-indent: 2em;
text-align: justify;
text-justify: inter-word;
hyphens: auto;
overflow-wrap: break-word;
}
</style>
<div id="body" class="page-base">
<h1>Pearl Street Cafe</h1>
<h2>Short Story by <strong>Bram Noidz</strong></h2>
<p class="drop-cap">Trevor woke up at the usual time, naturally, without an alarm clock. A lot of podcasts are discussing the topic of mental health, and apparently waking up with an alarm is bad for anxiety. It took a couple of weeks but Trevor finally tuned his circadian rhythms to obey his schedule, rather than the other way around. He flops out of bed.</p>
<p>The brain fog was especially thick this morning. Trevor went through the process of malaise attribution. Perhaps it was because of the six o'clock coffee the evening before? Or could it be work-induced stress? He had just recovered from a bout of illness due to the latest strain of respiratory viruses circulating around. Maybe that was it. Nothing the cold plunge can't thaw. Trevor prepared the ice bath while contemplating whether the social stigma around caffeine addiction is morally justified.</p>
<p>With the morning routine out of the way, it's time to grind. Work must follow every morning routine, otherwise there is no point to the routine in the first place.</p>
<p>Trevor grabbed his 15-inch MacBook Pro from the nightstand and stuffed it into his messenger bag. He boarded his self-driving SUV and hitched a ride by himself to the local coffee shop. Pearl Street has about three or four coffee shops that are worth the time spent indoors, and several others that are not. Only two of them have WiFi that is reliable enough for Laptop Work. And out of those two, only one of them actually has coffee that tastes beanworthy. <em>Navigation complete.</em></p>
<p>The barista got to work on Trevor's double shot, low foam latté. He takes a seat in the corner of the café where the best reception is available. It always takes a few minutes after opening up the laptop before Trevor remembers what his job actually is. Something with numbers. <em>A transponster?</em> Colleagues whom he's never actually met had sent messages during his cold plunge and while he was sleeping soundly, and reading them allows the work gets context switched back into local memory. Sometimes he wonders if <em>Ms. Trish</em> and <em>Mr. Herb</em> are actually North Korean remote workers, scamming fiat to fund the regime. He realizes that he doesn't care.</p>
<p>Two hours fly by in an instant. Almost time for lunch. Usually it is only the Numbers and lunch that occupy Trevor's mind at this time of the day. But this time he was feeling pensive for some reason. Mom once asked Trevor what he actually did at his job. She worked in a grocery store with her hands so she wanted to know the concrete details about what he did during the day that let him put food on the table. Ultimately it just came down to typing and clicking on a computer. That's it? Someone's paying for it so it must be worth something.</p>
<p>Trevor was halfway through eating his <em>Spam Sandwich</em> when everything came crashing down like a house of cards. Production was not even remotely the point of his job. A modern economy functions much like an electronic circuit, where electrons move from one point of high electric potential towards another point with lower electric potential. Without this difference in potential between two points, the circuit is inert and useless. If Trevor wasn't welding steel beams or fixing toilets, then he must be located in the opposite polarity. <em>An electron sink. A ground prong. A consumer.</em></p>
<p>So what, then, is the point of doing the Numbers? Sending messages to <em>Ms. Trish</em> and <em>Mr. Herb</em>? It's to justify Trevor's consumption. Consumption without work, no matter how fake the work is, is not sustainable after millions of years of cultural evolution that put selective pressure on becoming a productive member of society. Trevor's job is not the Numbers. It's the Spam Sandwich.</p>
<p>Terror turns into loathing, and loathing turns into acceptance. Tyler Durden took a different path halfway through this revelation, but Trevor's feels more peaceful and more righteous. Wonder what's on TV tonight. <span class="endmark"></span></p>
</div>

148
pages/vranklevictim.html Normal file
View File

@@ -0,0 +1,148 @@
<meta charset="UTF-8">
<style>
body {
width: 5.5in;
height: 8.5in;
position: relative;
font-family: sans-serif;
}
h1,h2,h3 {
text-align: center;
margin-top: 8px;
margin-bottom: 8px;
}
h1 {
font-size: 4rem;
font-weight: bold;
font-family: "Impact";
color: darkred;
rotate: 5deg;
border: solid;
border-width: 0.125in;
padding: 8px;
border-radius: 16px;
}
h2 {
margin-left: 0.25in;
margin-right: 0.25in;
font-size: 1.4rem;
}
p {
margin-top: 4px;
margin-bottom: 4px;
margin-left: 0.25in;
margin-right: 0.25in;
text-align: justify;
}
.root {
height: 100%;
width: 100%;
background: linear-gradient(
to bottom,
grey 0%,
black 45%,
black, black
);
color: white;
display: flex;
flex-direction: column;
align-items: center;
}
div * {
z-index: 2;
}
img {
margin-top: 2.85in;
width: 50%;
position: absolute;
z-index: 1;
rotate: -10deg;
scale: 115%;
filter: saturate(0.45) sepia(0.8) brightness(0.6);
}
aside {
width: 100%;
text-transform: uppercase;
text-align: center;
margin-left: 0.25in;
margin-right: 0.25in;
}
figure {
margin: 0px;
padding: 0px;
}
.logo {
text-transform: uppercase;
background-image: radial-gradient(yellow, goldenrod);
color: transparent;
background-clip: text;
font-family: sans-serif;
font-weight: bold;
}
.byline {
background-color: #F0E0D6;
position: absolute;
right: 0px;
bottom: 0px;
font-size: 1em;
}
</style>
<div class="root">
<aside style="position: absolute; top: 0;">
Coming exclusively to <span class="logo">ULTRA-PLUS®</span> on Christmas Day
</aside>
<figure style="height: 0.3in"></figure>
<h1>
Vrankle: A Victim
</h1>
<img src="assets/img/vrankle.png"/>
<h2>
The true story of an innocent man's struggle
</h2>
<p>
Jer Vrankle was attending a fun demonstration downtown when a brute ignorantly called his baseball cap "cool" in the co-op grocer's parking lot. Vrankle was thrust into crisis. Recovering from a small cold and running a mild fever, Jer was the opposite of "cool". The property security dismissed complaints about the comment.
</p>
<figure style="height: 2.5in;"></figure>
<p>
The 48 episode docuseries, directed by the award-winning Selgus Bango, deals with the complex struggles plaguing society today: relationships, decency, and verbal communication.
</p>
<p>
Learn about every aspect of the conflict. Make up your own mind about the innocent Jer and antagonistic brute. Hear interviews with bystanders parking in the lot, watch the security camera footage of the event, and listen to Ivy League professors unpack every detail.
</p>
<p>
Famed composer Hilden Valeigh's soundtrack is a complex mix of droning repetitive ambiance. The 9 disc soundtrack is releasing exclusively for streaming on <span class="logo">ULTRA-PLUS®</span> later this winter.
</p>
<aside style="position: absolute; bottom: 32px;">
Watch the 48 episode docuseries on <span class="logo">ULTRA-PLUS®</span>
</aside>
<div class="byline">
<span style="color: black;">by</span>
<span style="color: black;">[+]</span>
<span style="font-weight: bold; color: forestgreen;">Max Res Default</span>
</div>
</div>

19
pages/windows-shirt.html Normal file
View File

@@ -0,0 +1,19 @@
<style>
#windows-shirt::before {
background-image: url("assets/img/windows-shirt.jpg");
}
#windows-shirt-caption {
position: absolute;
bottom: 10%;
left: 10%;
}
</style>
<div id="windows-shirt" class="page-base interlude">
<div class="interlude-caption" id="windows-shirt-caption">
we line up to store<br/>
only 4 gigabytes allowed<br/>
comrade, be grateful!
</div>
</div>

136
pages/wtf.html Normal file
View File

@@ -0,0 +1,136 @@
<style>
#wtf-page {
background: url("assets/img/wtf.png") no-repeat center center;
background-size: cover;
font: 8.75pt/1.40 Tahoma, sans-serif;
padding: 10px;
hyphens: auto;
overflow-wrap: break-word;
}
h2 {
font-family: "Heading Now";
font-size: 23px;
}
h3 {
font-family: "Heading Now";
font-size: 24px;
text-align: right;
}
p {
font-family: Helvetica, sans-serif;
font-size: 12.75pt;
}
/* Windows 98 styles */
.win98-window {
background: #c0c0c0c0;
border: 2px solid;
border-color: #ffffff #404040 #404040 #ffffff; /* light top/left, dark bottom/right */
padding: 6px;
/* Vertically center the window in the page */
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.win98-titlebar {
display: flex;
align-items: center;
margin: 0 0 6px 0;
padding: 4px 8px;
min-height: 22px;
font-weight: bold;
color: #ffffff;
background: linear-gradient(90deg, #000080 0%, #1084d0 100%);
border: 2px solid;
border-color: #ffffff #404040 #404040 #ffffff;
}
.win98-content {
background: #ffffffc0;
border: 2px solid;
border-color: #808080 #ffffff #ffffff #808080; /* inset look */
padding: 10px;
color: #000000;
line-height: 1.4;
}
.feces {
transform: skewX(-10deg);
transform-origin: bottom center;
display: inline-block;
color: rgb(154, 77, 0);
font-stretch: condensed;
font-family: inherit;
font-size: 90%;
font-weight: bold;
}
.round-boxes {
display: inline-block;
font-stretch: condensed;
font-family: inherit;
border-radius: 10px;
background: rgb(166, 205, 211);
padding: 2px 4px;
}
.real {
display: inline-block;
transform: scaleX(2.5);
transform-origin: left;
font-weight: bold;
width: 80px;
}
</style>
<div id="wtf-page" class="page-base">
<div class="win98-window">
<h1 class="win98-titlebar">wtf??? is this???</h1>
<div id="wtf-text" class="win98-content">
<h2>AH, THE CITY BY THE BAY,</h2>
<p>
If you ask me, I say the city needs some more grime. I
know thats probably not what you were thinking when
you stepped over the <span class="feces">HUMAN FECES</span>
this morning on the
way from picking up your $6 latte. And for damn sure it
wasnt what your UX designer or whatever he calls himself
was thinking when he was drawing his stupid fucking
<span class="round-boxes">ROUND BOXES</span> for the hundreth time.
</p>
<p>
Put the round boxes on ice. This is SMART BAR. The
“mission district before it was cool” publication. The
“noisebridge before they got kicked out” edition.
</p>
<p>
You see, here at SMART BAR we dont give a shit. But we
also care. No, not about you. We have our own agenda.
What is our agenda? Thats our business.
</p>
<p>
SMART BAR is the little bit o dirt that makes virtual
reality feel more <span class="real">real</span>. Its the disgusting mess of
cables behind your desk that youre constantly trying
to hide. Its the malware with the pretty UI that tries to
steal peoples bitcoin.
</p>
<p>Log on, if you dare.</p>
<h3>TRENT SNEEK</h3>
</div>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />

View File

@@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />

View File

@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Print</title> <title>Print</title>
<link rel="stylesheet" href="assets/css/base.css" /> <link rel="stylesheet" href="assets/css/base.css" />
<link rel="stylesheet" href="assets/css/smartbar.css" />
<style> <style>
/* Ensure a clean print with no gaps between pages */ /* Ensure a clean print with no gaps between pages */
@media screen { body { background: #eeeeee; } } @media screen { body { background: #eeeeee; } }