resume_page

Log | Files | Refs | README

commit 42b6815277209693b0df9900b970ff32f6a17b8a
Author: William Lindholm <william_lindholm@outlook.com>
Date:   Sun, 20 Sep 2026 23:22:54 +0200

Created Curriculum vitae page

Diffstat:
A.gitignore | 6++++++
AREADME.md | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/Dockerfile | 14++++++++++++++
Aapp/index.html | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/main.py | 21+++++++++++++++++++++
Aapp/style.css | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocker-compose.yml | 19+++++++++++++++++++
7 files changed, 365 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,6 @@ +__pycache__/ +*.py[cod] +.venv/ +.env +.DS_Store +Thumbs.db diff --git a/README.md b/README.md @@ -0,0 +1,135 @@ +# Start the container + +Install Docker with Docker Compose v2.23.0 or newer. From this directory, run: + +```sh +docker compose up -d +``` + +Open <http://localhost:8000>. + +To start with automatic application updates during development, run: + +```sh +docker compose watch +``` + +# Software Requirements Specification + +## Personal Résumé Website + +### 1. Intent + +Build a permanent, lightweight résumé website for a software developer. + +Treat the site as a document published on the Web, not as a web application. + +### 2. Application + +**REQ-01 — Runtime**\ +Use Python and Flask. Keep all Flask logic in `app/main.py`. + +**REQ-02 — Page**\ +The application shall contain exactly one page. Serve the résumé at `/`. Keep HTML in `app/index.html` and CSS in `app/style.css`. + +**REQ-03 — JavaScript**\ +Do not use JavaScript. + +### 3. Project Layout + +**REQ-04 — Files**\ +Use this structure: +``` +/ +├── .gitignore +├── README.md +├── docker-compose.yml +└── app/ + ├── main.py + ├── Dockerfile + ├── index.html + └── style.css +``` + +Do not add directories. + +**REQ-05 — README**\ +The README shall contain only: + +1. Instructions to start the container. +2. This SRS. + +Place the start instructions first. + +### 4. Container + +**REQ-06 — Operation**\ +Package the application with Docker. `docker compose up -d` shall start the site. Restarting the container shall restore normal operation. + +**REQ-07 — Compose Watch**\ +Configure `develop.watch` in `docker-compose.yml`. `docker compose watch` shall detect changes to application files and update or rebuild the service as required. + +**REQ-08 — Runtime Dependencies**\ +Do not require external CDNs, hosted fonts, analytics, databases, build systems, JavaScript packages, or third-party runtime services. + +### 5. Visual Design + +**REQ-09 — Direction**\ +Use the visual restraint of long-lived FOSS and Unix websites. Suitable references include CRUX Linux, kernel.org, OpenBSD, NetBSD, suckless.org, SQLite, musl libc, BusyBox, Alpine Linux, and SourceHut. Do not copy a reference site. + +Do not imitate a terminal. Do not use fake prompts, simulated commands, or monospace text as decoration. + +**REQ-10 — Palette**\ +Use this palette: +```less +Accent blue #007acc +Light highlight #d4d4d4 +Primary text #333333 +Dark highlight #252525 +Dark background #1e1e1e +``` + +Declare colors as custom properties in `:root`. Reference those properties throughout the stylesheet. Use accent blue mainly for links and small accents. + +**REQ-11 — Typography**\ +Use system or web-safe fonts only. Declare each font stack once as a custom property in `:root`. + +**REQ-12 — Style**\ +Use typography, spacing, and hierarchy to structure the page. Avoid gradients, shadows, glass effects, animation, oversized hero sections, card grids, carousels, and decorative image galleries. + +### 6. Content and Layout + +**REQ-13 — Sections**\ +Include: + +- Introduction +- Work experience +- Education +- Selected projects +- Technical skills +- Contact and external links + +Present experience and projects as concise text with ordinary links. + +**REQ-14 — Responsive Layout**\ +Use a restrained maximum width on large displays. Use one readable column on small displays. Require no JavaScript for responsive behavior. + +### 7. Document Quality + +**REQ-15 — HTML**\ +Use semantic HTML5 and a logical heading order. The document shall remain readable without CSS. + +**REQ-16 — CSS**\ +Keep the stylesheet small and conventional. Prefer standard layout methods over frameworks or design-system abstractions. + +**REQ-17 — Priorities**\ +Prioritize, in order: + +1. Maintainability +2. Accessibility +3. Readability +4. Browser compatibility +5. Load time +6. Visual character + +When several solutions meet a requirement, use the simplest one. diff --git a/app/Dockerfile b/app/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.13-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app +RUN pip install --no-cache-dir Flask==3.1.2 waitress==3.0.2 \ + && useradd --uid 10001 --create-home resume \ + && chown resume:resume /app + +COPY --chown=resume:resume main.py index.html style.css ./ +USER resume +EXPOSE 8000 +CMD ["python", "main.py"] diff --git a/app/index.html b/app/index.html @@ -0,0 +1,96 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="description" content="[sample]Alex Morgan — software developer. Résumé, selected projects, and contact information.[endsample]"> + <title>[sample]Alex Morgan — Résumé[endsample]</title> + <link rel="stylesheet" href="/style.css"> +</head> +<body> + <a class="skip-link" href="#main">Skip to résumé</a> + <header> + <p class="document-label">Curriculum vitae</p> + <h1><span class="sample-marker">[sample]</span>Alex Morgan<span class="sample-marker">[endsample]</span></h1> + <p class="subtitle">[sample]Software developer · Stockholm, Sweden[endsample]</p> + <nav aria-label="Résumé sections"> + <a href="#experience">Experience</a> + <a href="#education">Education</a> + <a href="#projects">Projects</a> + <a href="#skills">Skills</a> + <a href="#contact">Contact</a> + </nav> + </header> + + <main id="main"> + <section aria-labelledby="introduction"> + <h2 id="introduction">Introduction</h2> + <p>[sample]I build dependable software for the web, with a focus on Python, Linux, and straightforward systems. I enjoy turning complicated requirements into tools that are easy to use, understand, and maintain.[endsample]</p> + <p>[sample]My work spans backend services, developer tooling, and the small details that make software reliable over time.[endsample]</p> + </section> + + <section aria-labelledby="experience"> + <h2 id="experience">Work experience</h2> + <article> + <h3>[sample]Software developer · Northline Systems[endsample]</h3> + <p class="entry-meta">[sample]2022–present · Stockholm, Sweden[endsample]</p> + <p>[sample]Develop and maintain Python services for an internal operations platform. Work with a small team on API design, database changes, and production support.[endsample]</p> + <ul> + <li>[sample]Simplified background processing and added checks that made failed jobs easier to diagnose and recover.[endsample]</li> + <li>[sample]Documented deployment procedures and maintained automated tests for critical workflows.[endsample]</li> + </ul> + </article> + <article> + <h3>[sample]Junior software developer · Fieldwork Studio[endsample]</h3> + <p class="entry-meta">[sample]2020–2022 · Uppsala, Sweden[endsample]</p> + <p>[sample]Built accessible websites and small backend services. Collaborated with designers and clients, reviewed code, and supported applications after release.[endsample]</p> + </article> + </section> + + <section aria-labelledby="education"> + <h2 id="education">Education</h2> + <h3>[sample]BSc in Computer Science · Example University[endsample]</h3> + <p class="entry-meta">[sample]2017–2020 · Sweden[endsample]</p> + <p>[sample]Coursework in algorithms, operating systems, databases, and software engineering. Final project explored practical monitoring for small distributed services.[endsample]</p> + </section> + + <section aria-labelledby="projects"> + <h2 id="projects">Selected projects</h2> + <article> + <h3>[sample]Plainlog[endsample]</h3> + <p>[sample]A small command-line tool for searching and summarizing application logs. Designed around readable output, predictable behavior, and Unix pipes. <a href="https://example.com/plainlog">Project and source</a>.[endsample]</p> + <p class="entry-meta">[sample]Python · Linux · Automated testing[endsample]</p> + </article> + <article> + <h3>[sample]Community archive[endsample]</h3> + <p>[sample]A lightweight, searchable archive for a local association. Server-rendered pages, accessible forms, and a documented backup process keep day-to-day maintenance simple. <a href="https://example.com/community-archive">Project and source</a>.[endsample]</p> + <p class="entry-meta">[sample]Python · Flask · SQLite · HTML &amp; CSS[endsample]</p> + </article> + </section> + + <section aria-labelledby="skills"> + <h2 id="skills">Technical skills</h2> + <dl> + <dt>Languages</dt> + <dd>[sample]Python, SQL, HTML, CSS, shell scripting[endsample]</dd> + <dt>Tools &amp; systems</dt> + <dd>[sample]Linux, Git, Docker, PostgreSQL, SQLite[endsample]</dd> + <dt>Practice</dt> + <dd>[sample]API design, automated testing, accessibility, technical documentation[endsample]</dd> + </dl> + </section> + + <section aria-labelledby="contact"> + <h2 id="contact">Contact and external links</h2> + <address> + <p>[sample]Email: <a href="mailto:alex@example.com">alex@example.com</a>[endsample]</p> + <p>[sample]<a href="https://example.com/alex/source">Source code profile</a> · <a href="https://example.com/alex/profile">Professional profile</a>[endsample]</p> + </address> + </section> + </main> + + <footer> + <p>Sample résumé. All information enclosed in [sample] and [endsample] is illustrative.</p> + </footer> +</body> +</html> diff --git a/app/main.py b/app/main.py @@ -0,0 +1,21 @@ +"""Serve the résumé document and its stylesheet.""" + +from flask import Flask, send_from_directory + +app = Flask(__name__, static_folder=None) + + +@app.get("/") +def resume(): + return send_from_directory(app.root_path, "index.html") + + +@app.get("/style.css") +def stylesheet(): + return send_from_directory(app.root_path, "style.css") + + +if __name__ == "__main__": + from waitress import serve + + serve(app, host="0.0.0.0", port=8000) diff --git a/app/style.css b/app/style.css @@ -0,0 +1,74 @@ +:root { + color-scheme: light; + --accent-blue: #007acc; + --light-highlight: #d4d4d4; + --primary-text: #333333; + --dark-highlight: #252525; + --dark-background: #1e1e1e; + --paper: #ffffff; + --font-body: Georgia, "Times New Roman", serif; + --font-heading: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; +} + +* { box-sizing: border-box; } + +html { background: var(--paper); } + +body { + max-width: 50rem; + margin: 0 auto; + padding: 3rem 1.5rem 2rem; + color: var(--primary-text); + font-family: var(--font-body); + font-size: 1.0625rem; + line-height: 1.65; + overflow-wrap: anywhere; +} + +header { border-bottom: 2px solid var(--dark-background); padding-bottom: 1.5rem; } + +h1, h2, h3, nav, .document-label, .entry-meta, dt, footer { + font-family: var(--font-heading); +} + +h1, h2, h3 { color: var(--dark-highlight); line-height: 1.35; } +h1 { margin: 0.25rem 0; font-size: 2.25rem; font-weight: 650; } +h2 { margin: 0 0 1rem; font-size: 1.25rem; } +h3 { margin: 0 0 0.25rem; font-size: 1rem; font-weight: 650; } +p { margin: 0 0 0.8rem; } +.document-label { margin: 0; font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase; } +.sample-marker { font-size: 0.75rem; font-weight: 400; vertical-align: middle; } +.subtitle { margin-bottom: 1.25rem; } +nav { display: flex; flex-wrap: wrap; gap: 0.5rem 1.4rem; font-size: 0.9rem; } +a { color: var(--accent-blue); text-underline-offset: 0.18em; } +a:hover { color: var(--dark-highlight); } +a:focus-visible { outline: 2px solid var(--dark-highlight); outline-offset: 4px; } +section { padding: 1.75rem 0; border-bottom: 1px solid var(--light-highlight); } +section > :last-child, article > :last-child { margin-bottom: 0; } +article + article { margin-top: 1.5rem; } +.entry-meta { margin-bottom: 0.65rem; font-size: 0.85rem; } +ul { margin: 0.5rem 0 0; padding-left: 1.35rem; } +li + li { margin-top: 0.35rem; } +dl { margin: 0; } +dt { font-size: 0.9rem; font-weight: 650; } +dd { margin: 0 0 0.75rem; } +dd:last-child { margin-bottom: 0; } +address { font-style: normal; } +footer { padding-top: 1.25rem; font-size: 0.8rem; } +.skip-link { position: absolute; top: -5rem; padding: 0.5rem; background: var(--paper); } +.skip-link:focus { top: 0.5rem; } + +@media (max-width: 36rem) { + body { padding: 1.5rem 1rem; } + h1 { font-size: 1.8rem; } + .sample-marker { display: block; } + section { padding: 1.5rem 0; } +} + +@media print { + body { max-width: none; padding: 0; font-size: 10pt; } + nav, .skip-link { display: none; } + a { color: var(--primary-text); } + h2, h3 { break-after: avoid; } + article { break-inside: avoid; } +} diff --git a/docker-compose.yml b/docker-compose.yml @@ -0,0 +1,19 @@ +services: + resume: + build: ./app + ports: + - "8000:8000" + restart: unless-stopped + develop: + watch: + - action: sync+restart + path: ./app/main.py + target: /app/main.py + - action: sync + path: ./app/index.html + target: /app/index.html + - action: sync + path: ./app/style.css + target: /app/style.css + - action: rebuild + path: ./app/Dockerfile