TOPIC: CSS
Blocking thin scrollbar styles in Thunderbird on Linux Mint
When you get a long email, you need to see your reading progress as you work your way through it. Then, the last thing that you need is to have someone specifying narrow scrollbars in the message HTML like this:
<html style="scrollbar-width: thin;">
This is what I with an email newsletter on AI Governance sent to me via Substack. Thankfully, that behaviour can be disabled in Thunderbird. While my experience was on Linux Mint, the same fix may work elsewhere. The first step is to navigate the menus to where you can alter the settings: "Hamburger Menu" > Settings > Scroll to the bottom > Click on the Config Editor button.
In the screen that opens, enter layout.css.scrollbar-width-thin.disabled in the search and press the return key. Should you get an entry (and I did), click on the arrows button to the right to change the default value of False to True. Should your search be fruitless, right click anywhere to get a context menu where you can click on New and then Boolean to create an entry for layout.css.scrollbar-width-thin.disabled, which you then set to True. Whichever way you have accomplished the task, restarting Thunderbird ensures that the setting applies.
If the default scrollbar thickness in Thunderbird is not to your liking, returning to the Config Editor will address that. Here, you need to search for or create widget.non-native-theme.scrollbar.size.override. Since this takes a numeric value, pick the appropriate type if you are creating a new entry. Since that was not needed in my case, I pressed the edit button, chose a larger number and clicked on the tick mark button to confirm it. The effect was seen straight and all was how I wanted it.
In the off chance that the above does not work for you, there is one more thing that you can try, and this is specific to Linux. It sends you to the command line, where you issue this command:
gsettings get org.gnome.desktop.interface overlay-scrolling
Should that return a value of true, follow the with this command to change the setting to false:
gsettings set org.gnome.desktop.interface overlay-scrolling false
After that, you need to log off and back on again for the update to take effect. Since I had no recourse to that, it may be the same for you too.
Building a modular Hugo website home page using block-driven front matter
Inspired by building a modular landing page on a Grav-powered subsite, I wondered about doing the same for a Hugo-powered public transport website that I have. It was part of an overall that I was giving it, with AI consultation running shotgun with the whole effort. The home page design was changed from a two-column design much like what was once typical of a blog, to a single column layout with two-column sections.
The now vertical structure consisted of numerous layers. First, there is an introduction with a hero image, which is followed by blocks briefly explaining what the individual sections are about. Below them, two further panels describe motivations and scope expansions. After those, there are two blocks displaying pithy details of recent public transport service developments before two final panels provide links to latest articles and links to other utility pages, respectively.
This was a conscious mix of different content types, with some nesting in the structure. Much of the content was described in page front matter, instead of where it usually goes. Without that flexibility, such a layout would not have been possible. All in all, this illustrates just how powerful Hugo is when it comes to constructing website layouts. The limits essentially are those of user experience and your imagination, and necessarily in that order.
On Hugo Home Pages
Building a home page in Hugo starts with understanding what content/_index.md actually represents. Unlike a regular article file, _index.md denotes a list page, which at the root of the content directory becomes the site's home page. This special role means Hugo treats it differently from a standard single page because the home is always a list page even when the design feels like a one-off.
Front matter in content/_index.md can steer how the page is rendered, though it remains entirely optional. If no front matter is present at all, Hugo still creates the home page at .Site.Home, draws the title from the site configuration, leaves the description empty unless it has been set globally, and renders any Markdown below the front matter via .Content. That minimal behaviour suits sites where the home layout is driven entirely by templates, and it is a common starting point for new projects.
How the Underlying Markdown File Looks
While this piece opens with a description of what was required and built, it is better to look at the real _index.md file. Illustrating the block-driven pattern in practical use, here is a portion of the file:
---
title: "Maximising the Possibilities of Public Transport"
layout: "home"
blocks:
- type: callout
text1: "Here, you will find practical, thoughtful insight..."
text2: "You can explore detailed route listings..."
image: "images/sam-Up56AzRX3uM-unsplash.jpg"
image_alt: "Transpennine Express train leaving Manchester Piccadilly train station"
- type: cards
heading: "Explore"
cols_lg: 6
items:
- title: "News & Musings"
text: "Read the latest articles on rail networks..."
url: "https://ontrainsandbuses.com/news-and-musings/"
- title: "News Snippets"
...
- type: callout
heading: "Motivation"
text2: "Since 2010, British public transport has endured severe challenges..."
image: "images/joseph-mama-aaQ_tJNBK4c-unsplash.jpg"
image_alt: "Buses in Leeds, England, U.K."
- type: callout
heading: "An Expanding Scope"
text2: "You will find content here drawn from Ireland..."
image: "images/snap-wander-RlQ0MK2InMw-unsplash.jpg"
image_alt: "TGV speeding through French countryside"
---
There are several things that are worth noting here. The title and layout: "home" fields appear at the top, with all structural content expressed as a blocks list beneath them. There is no Markdown body because the blocks supply all the visible content, and the file contains no layout logic of its own, only a description of what should appear and in what order. However, the lack of a Markdown body does pose a challenge for spelling and grammar checking using the LanguageTool extension in VSCode, which means that you need to ensure that proofreading needs to happen in a different way, such as using the editor that comes with the LanguageTool browser extension.
Template Selection and Lookup Order
Template selection is where Hugo's home page diverges most noticeably from regular sections. In Hugo v0.146.0, the template system was completely overhauled, and the lookup order for the home page kind now follows a straightforward sequence: layouts/home.html, then layouts/list.html, then layouts/all.html. Before that release, the conventional path was layouts/index.html first, falling back to layouts/_default/list.html, and the older form remains supported through backward-compatibility mapping. In every case, baseof.html is a wrapper rather than a page template in its own right, so it surrounds whichever content template is selected without substituting for one.
The choice of template can be guided further through front matter. Setting layout: "home" in content/_index.md, as in the example above, encourages Hugo to pick a template named home.html, while setting type: "home" enables more specific template resolution by namespace. These are useful options when the home page deserves its own template path without disturbing other list pages.
The Home Template in Practice
With the front matter established, the template that renders it is worth examining in its own right. It happens that the home.html for this site reads as follows:
<!DOCTYPE html>
{{- partial "head.html" . -}}
<body>
{{- partial "header.html" . -}}
<div class="container main" id="content">
<div class="row">
<h2 class="centre">{{ .Title }}</h2>
{{- partial "blocks/render.html" . -}}
</div>
{{- partial "recent-snippets-cards.html" . -}}
{{- partial "home-teasers.html" . -}}
{{ .Content }}
</div>
{{- partial "footer.html" . -}}
{{- partial "cc.html" . -}}
{{- partial "matomo.html" . -}}
</body>
</html>
This template is self-contained rather than wrapping a base template. It opens the full HTML document directly, calls head.html for everything inside the <head> element and header.html for site navigation, then establishes the main content container. Inside that container, .Title is output as an h2 heading, drawing from the title field in content/_index.md. The block dispatcher partial, blocks/render.html, immediately follows and is responsible for looping through .Params.blocks and rendering each entry in sequence, handling all the callout and cards blocks described in the front matter.
Below the blocks, two further partials render dynamic content independently of the front matter. recent-snippets-cards.html displays the two most recent news snippets as full-content cards, while home-teasers.html presents a compact linked list of recent musings alongside a weighted list of utility pages. After those, {{ .Content }} outputs any Markdown written below the front matter in content/_index.md, though in this case, the file has no body content, so nothing is rendered at that point. The template closes with footer.html, a cookie notice via cc.html and a Matomo analytics snippet.
Notice that this template does not use {{ define "main" }} and therefore does not rely on baseof.html at all. It owns the full document structure itself, which is a legitimate approach when the home page has a sufficiently distinct shape that sharing a base template would add complexity rather than reduce it.
The Block Dispatcher
The blocks/render.html partial is the engine that connects the front matter to the individual block templates. Its full content is brief but does considerable work:
{{ with .Params.blocks }}
{{ range . }}
{{ $type := .type | default "text" }}
{{ partial (printf "blocks/%s.html" $type) (dict "page" $ "block" .) }}
{{ end }}
{{ end }}
The with .Params.blocks guard means the entire loop is skipped cleanly if no blocks key is present in the front matter, so pages that do not use the system are unaffected. For each block in the list, the type field is read and passed through printf to build the partial path, so type: callout resolves to blocks/callout.html and type: cards resolves to blocks/cards.html. If a block has no type, the fallback is text, so a blocks/text.html partial would handle it. The dict call constructs a fresh context map passing both the current page (as page) and the raw block data (as block) into the partial, keeping the two concerns cleanly separated.
The Callout Blocks
The callout.html partial renders bordered, padded sections that can carry a heading, an image and up to five paragraphs of text. Used for the website introduction, motivation and expanded scope sections, its template is as follows:
{{ $b := .block }}
<section class="mt-4">
<div class="p-4 border rounded">
{{ with $b.heading }}<h3>{{ . }}</h3>{{ end }}
{{ with $b.image }}
<img
src="{{ . }}"
class="img-fluid w-100 rounded"
alt="{{ $b.image_alt | default "" }}">
{{ end }}
<div class="text-columns mt-4">
{{ with $b.text1 }}<p>{{ . }}</p>{{ end }}
{{ with $b.text2 }}<p>{{ . }}</p>{{ end }}
{{ with $b.text3 }}<p>{{ . }}</p>{{ end }}
{{ with $b.text4 }}<p>{{ . }}</p>{{ end }}
{{ with $b.text5 }}<p>{{ . }}</p>{{ end }}
</div>
</div>
</section>
The pattern here is consistent and deliberate. Every field is wrapped in a {{ with }} block, so fields absent from the front matter produce no output and no empty elements. The heading renders as an h3, sitting one level below the page's h2 title and maintaining a coherent document outline. The image uses img-fluid and w-100 alongside rounded, making it fully responsive and visually consistent with the bordered container. According to the Bootstrap documentation, img-fluid applies max-width: 100% and height: auto so the image scales with its parent, while w-100 ensures it fills the container width regardless of its intrinsic size. The image_alt field falls back to an empty string via | default "" rather than omitting the attribute entirely, which keeps the rendered HTML valid.
Text content sits inside a text-columns wrapper, which allows a stylesheet to apply a CSS multi-column layout to longer passages without altering the template. The numbered paragraph fields text1 through text5 reflect the varying depth of the callout blocks in the front matter: the introductory callout uses two paragraphs, while the Motivation callout uses four. Adding another paragraph field to a block requires only a new {{ with $b.text6 }} line in the partial and a matching text6 key in the front matter entry.
The Section Introduction Blocks
The cards.html partial renders a headed grid of linked blocks, with the column width at large viewports driven by a front matter parameter. This is used for the website section introductions and its template is as follows:
{{ $b := .block }}
{{ $colsLg := $b.cols_lg | default 4 }}
<section class="mt-4">
{{ with $b.heading }}<h3 class="h4 mb-3">{{ . }}</h3>{{ end }}
<div class="row">
{{ range $b.items }}
<div class="col-12 col-md-6 col-lg-{{ $colsLg }} mb-3">
<div class="card h-100 ps-2 pe-2 pt-2 pb-2">
<div class="card-body">
<h4 class="h5 card-title mt-1 mb-2">
<a href="{{ .url }}">{{ .title }}</a>
</h4>
{{ with .text }}<p class="card-text mb-0">{{ . }}</p>{{ end }}
</div>
</div>
</div>
{{ end }}
</div>
</section>
The cols_lg value defaults to 4 if not specified, which produces a three-column grid at large viewports using Bootstrap's twelve-column grid. The transport site's cards block sets cols_lg: 6, giving two columns at large viewports and making better use of the wider reading space for six substantial card descriptions. At medium viewports, the col-md-6 class produces two columns regardless of cols_lg, and col-12 ensures single-column stacking on small screens.
The heading uses the h4 utility class on an h3 element, pulling the visual size down one step while keeping the document outline correct, since the page already has an h2 title and h3 headings in the callout blocks. Each card title then uses h5 on an h4 for the same reason. The h-100 class on the card sets its height to one hundred percent of the column, so all cards in a row grow to match the tallest one and baselines align even when descriptions vary in length. The padding classes ps-2 pe-2 pt-2 pb-2 add a small inset without relying on custom CSS.
Brief Snippets of Recent Public Transport Developments
The recent-snippets-cards.html partial sits outside the blocks system and renders the most recent pair of short transport news posts as full-content cards. Here is its template:
<h3 class="h4 mt-4 mb-3">Recent Snippets</h3>
<div class="row">
{{ range ( first 2 ( where .Site.Pages "Type" "news-snippets" ) ) }}
<div class="col-12 col-md-6 mb-3">
<div class="card h-100">
<div class="card-body">
<h4 class="h6 card-title mt-1 mb-2">
{{ .Date.Format "15:04, January 2" }}<sup>{{ if eq (.Date.Format "2") "2" }}nd{{ else if eq (.Date.Format "2") "22" }}nd{{ else if eq (.Date.Format "2") "1" }}st{{ else if eq (.Date.Format "2") "21" }}st{{ else if eq (.Date.Format "2") "3" }}rd{{ else if eq (.Date.Format "2") "23" }}rd{{ else }}th{{ end }}</sup>, {{ .Date.Format "2006" }}
</h4>
<div class="snippet-content">
{{ .Content }}
</div>
</div>
</div>
</div>
{{ end }}
</div>
The where function filters .Site.Pages to the news-snippets content type, and first 2 takes only the two most recently created entries. Notably, this collection does not call .ByDate.Reverse before first, which means it relies on Hugo's default page ordering. Where precise newest-first ordering matters, chaining ByDate.Reverse before first makes the intent explicit and avoids surprises if the default ordering changes.
The date heading warrants attention. It formats the time as 15:04 for a 24-hour clock display, followed by the month name and day number, then appends an ordinal suffix using a chain of if and else if comparisons against the raw day string. The logic handles the four irregular cases (1st, 21st, 2nd, 22nd, 3rd and 23rd) before falling back to th for all other days. The suffix is wrapped in a <sup> element so it renders as a superscript. The year follows as a separate .Date.Format "2006" call, separated from the day by a comma. Each card renders the full .Content of the snippet rather than a summary, which suits short-form posts where the entire entry is worth showing on the home page.
Latest Musings and Utility Pages Blocks
The home-teasers.html partial renders a two-column row of linked lists, one for recent long-form articles and one for utility pages. Its template is as follows:
<div class="row mt-4">
<div class="col-12 col-md-6 mb-3">
<div class="card h-100">
<div class="card-body">
<h3 class="h5 card-title mb-3">Recent Musings</h3>
{{ range first 5 ((where .Site.RegularPages "Type" "news-and-musings").ByDate.Reverse) }}
<p class="mb-2">
<a href="{{ .Permalink }}">{{ .Title }}</a>
</p>
{{ end }}
</div>
</div>
</div>
<div class="col-12 col-md-6 mb-3">
<div class="card h-100">
<div class="card-body">
<h3 class="h5 card-title mb-3">Extras & Utilities</h3>
{{ $extras := where .Site.RegularPages "Type" "extras" }}
{{ $extras = where $extras "Title" "ne" "Thank You for Your Message!" }}
{{ $extras = where $extras "Title" "ne" "Whoops!" }}
{{ range $extras.ByWeight }}
<p class="mb-2">
<a href="{{ .Permalink }}">{{ .Title }}</a>
</p>
{{ end }}
</div>
</div>
</div>
</div>
The left column uses .Site.RegularPages rather than .Site.Pages to exclude list pages, taxonomy pages and other non-content pages from the results. The news-and-musings type is filtered, sorted with .ByDate.Reverse and then limited to five entries with first 5, producing a compact, current list of article titles. The heading uses h5 on an h3 for the same visual-scale reason seen in the cards blocks, and h-100 on each card ensures the two columns match in height at medium viewports and above.
The right column builds the extras list through three chained where calls. The first narrows to the extras content type, and the subsequent two filter out utility pages that should never appear in public navigation, specifically the form confirmation and error pages. The remaining pages are then sorted by ByWeight, which respects the weight value set in each page's front matter. Pages without a weight default to zero, so assigning small positive integers to the pages that should appear first gives stable, editorially controlled ordering without touching the template.
Diagnosing Template Choices
Diagnosing which template Hugo has chosen is more reliable with tooling than with guesswork. Running the development server with debug output reveals the selected templates in the terminal logs. Another quick technique is to place a visible marker in a candidate file and inspect the page source.
HTML comments are often stripped during minified builds, and Go template comments never reach the output, so an innocuous meta tag makes a better marker because a minifier will not remove it. If the marker does not appear after a rebuild, either the template being edited is not in use because another file higher in the lookup order is taking precedence, or a theme is providing a matching file without it being obvious.
Front Matter Beyond Layout
Front matter on the home page earns its place when it supplies values that make their way into head tags and structured sections, rather than when it tries to replicate layout logic. A brief description is valuable for metadata and social previews because many base templates output it as a meta description tag. Where a site uses social cards, parameters for images and titles can be added and consumed consistently.
Menu participation also remains available to the home page, with entries in front matter allowing the home to appear in navigation with a given weight. Less common but still useful fields include outputs, which can disable or configure output formats, and cascade, which can provide defaults to child pages when site-wide consistency matters. Build controls can influence whether a page is rendered or indexed, though these are rarely changed on a home page once the structure has settled.
Template Hygiene
Template hygiene pays off throughout this process. Whether the home page uses a self-contained template or wraps baseof.html, the principle is the same: each file should own a clearly bounded responsibility. The home template in the example above does this well, with head.html, header.html and footer.html each handling their own concerns, and the main content area occupied by the blocks dispatcher and the two dynamic partials. Column wrappers are easiest to manage when each partial opens and closes its own structure, rather than relying on a sibling to provide closures elsewhere.
That self-containment prevents subtle layout breakage and means that adding a new block type requires only a small partial in layouts/partials/blocks/ and a new entry in the front matter blocks list, with no changes to any existing template. Once the home page adopts this pattern, the need for CSS overrides recedes because the HTML shape finally expresses intent instead of fighting it.
Bootstrap Utility Classes in Summary
Understanding Bootstrap's utility classes rounds off the technique because these classes anchor the modular blocks without the need for custom CSS. h-100 sets height to one hundred percent and works well on cards inside a flex row so that their bottoms align across a grid, as seen in both the cards block and the home teasers. The h4, h5 and h6 utilities apply a different typographic scale to any element without changing the document outline, which is useful for keeping headings visually restrained while preserving accessibility. img-fluid provides responsive behaviour by constraining an image to its container width and maintaining aspect ratio, and w-100 makes an image or any element fill the container width even if its intrinsic size would let it stop short. Together, these classes produce predictable and adaptable blocks that feel consistent across all viewports.
Closing Remarks
The result of combining Hugo's list-page model for the home, a block-driven front matter design and Bootstrap's light-touch utilities is a home page that reads cleanly and remains easy to extend. New block types become a matter of adding a small partial and a new blocks entry, with the dispatcher handling the rest automatically. Dynamic sections such as recent snippets sit in dedicated partials called directly from the template, updating without any intervention in content/_index.md. Existing sections can be reordered without editing templates, shared structure remains in one place, and the need for brittle CSS customisation fades because the templates do the heavy lifting.
A final point returns to content/_index.md. Keeping front matter purposeful makes it valuable. A title, a layout directive and a blocks list that models the editorially controlled page structure are often enough, as we have seen in this example from my public transport website. More seldom-used fields such as outputs, cascade and build remain available should a site require them, but their restraint reflects the wider approach: let content describe structure, let templates handle layout and avoid unnecessary complexity.
Understanding Bootstrap's twelve column grid for clean layouts
When it comes to designing your page structure using Bootstrap, you need to work within a twelve column grid. For uneven column widths, you need to make everything add up, while it is perhaps simpler when every column width is the same.
For instance, encountering a case of the latter when porting a website landing page as part of a migration from Textpattern to Grav meant having evenly sized columns, with one block for each section. To make everything up to twelve, two featured article blocks were added. What follows is a little about why that choice was made.
How the Twelve-Column System Works
Bootstrap's grid divides into twelve columns The number twelve was chosen because it has more divisors than any number before it or after it, up to sixty, making it exceptionally flexible for layout work.
<!-- Two across -->
<div class="col-12 col-md-6">...</div>
<!-- Three across -->
<div class="col-12 col-md-6 col-xl-4">...</div>
<!-- Classic blog layout: sidebar and main content -->
<div class="col-md-3">Sidebar</div>
<div class="col-md-9">Main content</div>
The column classes work by specifying how many of the twelve columns each element should span. For example, a col-6 class spans six columns (half the width), whilst col-4 spans four columns (one third). In the classic blog layout, this means using col-3 for a sidebar (one quarter width) and col-9 for main content (three quarters width). Other common combinations include col-8 and col-4 (two thirds and one third), or col-2 and col-10 (one sixth and five sixths).
For consistent column widths, certain numbers divide cleanly: col-12 (full width, one across), col-6 (half width, two across), col-4 (one third width, three across), col-3 (one quarter width, four across), col-2 (one sixth width, six across) and col-1 (one twelfth width, twelve across). When using 2, 3, 4, 6 or 12 blocks with these classes, the grid divides evenly. However, with other numbers, challenges emerge. For instance, eleven blocks in three columns leaves two orphans, whilst seven blocks in two columns creates uneven rows.
Bootstrap Breakpoints Explained
Bootstrap 5 defines six responsive breakpoints for different device categories:
| Breakpoint | Screen Width | Class Modifier |
|---|---|---|
| Extra small | Below 576px | None (just col-*) |
| Small | 576px and above | col-sm-* |
| Medium | 768px and above | col-md-* |
| Large | 992px and above | col-lg-* |
| Extra large | 1200px and above | col-xl-* |
| Extra extra large | 1400px and above | col-xxl-* |
These breakpoints cascade upwards. A class like col-md-6 applies from 768 pixels and continues to apply at all larger breakpoints unless overridden by a more specific class like col-xl-4. This cascading behaviour allows responsive layouts to be built with minimal markup, where each breakpoint only needs to specify what changes, rather than repeating the entire layout definition.
Putting It Into Practice
When column widths are equal, the implementation uses Bootstrap grid classes with a three-tier responsive system so that each block receives consistent treatment, with padding, borders and hover effects. Here is some boilerplate code showing how this can be accomplished:
<div class="row g-4 mt-3">
<div class="col-12 col-md-6 col-xl-4">
<div class="h-100">
<h3 class="mb-3">Irish Encounters</h3>
<p style="text-align:center" class="mt-3">
<img class="w-100 rounded" src="...">
</p>
<p>From your first arrival to hidden ferry crossings...</p>
<p>
<a href="/travel/ireland" class="btn btn-secondary mt-3 mb-3 shadow-none stretch">
Go and Have a Look: 12 Articles to Savour
</a>
</p>
</div>
</div>
<!-- Repeat for 9 more destination blocks -->
<!-- Then 2 featured article blocks -->
</div>
Two Bootstrap utility classes proved particularly useful here. Firstly, the h-100 class sets the height to 100% of the parent container, ensuring all blocks in a row have equal height regardless of content length. Meanwhile, the w-100 class sets the width to 100% of the parent container, making images fill their containers whilst maintaining aspect ratio when combined with responsive image techniques. Together, these help create visual consistency across the grid.
The responsive behaviour works as follows for twelve blocks:
| Screen Width | Class Used | Number of Columns | Number of Rows |
|---|---|---|---|
| Below 768px | col-12 |
1 | 12 |
| 768px and above | col-md-6 |
2 | 6 |
| 1200px and above | col-xl-4 |
3 | 4 |
The g-4 class adds consistent guttering between blocks across all breakpoints and is part of Bootstrap's spacing utilities, where the number (4) corresponds to a spacing value from Bootstrap's spacer scale. To accomplish this, the class applies gap spacing both horizontally and vertically between grid items, creating visual separation without needing to add margins to individual elements. This ensures blocks do not sit flush against each other whilst maintaining consistent spacing throughout the layout.
Taking Stock
Bootstrap's twelve-column grid works cleanly for certain block counts (1, 2, 3, 4, 6 and 12). In contrast, other numbers create visual imbalance in multi-column layouts. For this reason, the grid system should inform content decisions early in the planning process. Ultimately, planning block counts around the grid creates more harmonious layouts than forcing arbitrary numbers into place.
In this case, twelve blocks divided cleanly into the three-column grid, where other numbers would have created orphans. Beyond solving the layout challenge, featured articles provided value by drawing attention to important content whilst resolving the constraints of the grid system. The key takeaway is that content planning and grid design work together rather than in opposition.
Related Reading
For further exploration of these concepts, the Bootstrap Grid System documentation provides comprehensive coverage of the twelve-column system and its responsive capabilities. The Flexbox utilities documentation covers alignment and spacing options that complement the grid system.
Four technical portals that still deliver after decades online
The early internet was built on a different kind of knowledge sharing, one driven by individual expertise, community generosity and the simple desire to document what worked. Four informative websites that started in that era, namely MDN Web Docs, AskApache, WindowsBBS and Office Watch, embody that spirit and remain valuable today. They emerged at a time when technical knowledge was shared through forums, documentation and personal blogs rather than social media or algorithm-driven platforms, and their legacy persists in offering clarity and depth in an increasingly fragmented digital landscape.
MDN Web Docs
MDN Web Docs stands as a cornerstone of modern web development, offering comprehensive coverage of HTML, CSS, JavaScript and Web APIs alongside authoritative references for browser compatibility. Mozilla started the project in 2005 under the name Mozilla Developer Centre, and it has since grown into a collaborative effort of considerable scale. In 2017, Mozilla announced a formal partnership with Google, Microsoft, Samsung and the W3C to consolidate web documentation on a single platform, with Microsoft alone redirecting over 7,700 of its MSDN pages to MDN in that year.
For developers, the site is not merely a reference tool but a canonical guide that ensures standards are adhered to and best practices followed. Its tutorials, guides and learning paths make it indispensable for beginners and seasoned professionals alike. The site's community-driven updates and ongoing contributions from browser vendors have cemented its reputation as the primary source for anyone building for the web.
AskApache
AskApache is a niche but invaluable resource for those managing Apache web servers, built by a developer whose background lies in network security and penetration testing on shared hosting environments. The site grew out of the founder's detailed study of .htaccess files, which, unlike the main Apache configuration file httpd.conf, are read on every request and offer fine-grained, per-directory control without requiring root access to the server. That practical origin gives the content its distinctive character: these are not generic tutorials, but hard-won techniques born from real-world constraints.
The site's guides on blocking malicious bots, configuring caching headers, managing redirects with mod_rewrite and preventing hot-linking are frequently cited by system administrators and WordPress users. Its specificity and longevity have made it a trusted companion for those maintaining complex server environments, covering territory that mainstream documentation rarely touches.
WindowsBBS
WindowsBBS offers a clear window into the era when online forums were the primary hub for technical support. Operating in the tradition of classic bulletin board systems, the site has long been a resource for users troubleshooting Windows installations, hardware compatibility issues and malware removal. It remains completely free, sustained by advertisers and community donations, which reflects the ethos of mutual aid that defined early internet culture.
During the Windows XP and Windows 7 eras, community forums of this kind were essential for solving problems that official documentation often overlooked, with volunteers providing detailed answers to questions that Microsoft's own support channels would not address. While the rise of social media and centralised support platforms has reduced the prominence of such forums, WindowsBBS remains a testament to the power of community-driven problem-solving. Its straightforward structure, with users posting questions and experienced volunteers providing answers, mirrors the collaborative spirit that made the early web such a productive environment.
Office Watch
Office Watch has served as an independent source of Microsoft Office news, tips and analysis since 1996, making it one of the longer-running specialist publications of its kind. Its focus on Microsoft Office takes in advanced features and hidden tools that are seldom documented elsewhere, from lesser-known functions in Excel to detailed comparisons between Office versions and frank assessments of Microsoft's product decisions. That independence gives it a voice that official resources cannot replicate.
The site serves power users seeking to make the most of the software they use every day, with guides and books that extend its reach beyond the website itself. In an era where software updates are frequent and often poorly explained, Office Watch provides the kind of context and plain-spoken clarity that official documentation rarely offers.
The Enduring Value of Depth and Community
These four sites share a common thread: they emerged when technical knowledge was shared openly by experts and enthusiasts rather than filtered through algorithms or paywalls, and they retain the value that comes from that approach. Their continued relevance speaks to what depth, specificity and community can achieve in the digital world. While platforms such as Stack Overflow and GitHub Discussions have taken over many of the roles these sites once played, the original resources remain useful for their historical context and the quality of their accumulated content.
As the internet continues to evolve, the lessons from these sites are worth remembering. The most useful knowledge is often found at the margins, where dedicated individuals take the time to document, explain and share what they have learned. Whether you are a developer, a server administrator or an everyday Office user, these resources are more than archives: they are living repositories of expertise, built by people who cared enough to write things down properly.
Moves to Hugo
What amazes me is how things can become more complicated over time. As long as you knew HTML, CSS and JavaScript, building a website was not as onerous as long as web browsers played ball with it. Since then, things have got easier to use but more complex at the same time. One example is WordPress: in the early days, themes were much simpler than they are now. The web also has got more insecure over time, and that adds to complexity as well. It sometimes feels as if there is a choice to make between ease of use and simplicity.
It is against that background that I reassessed the technology that I was using on my public transport and Irish history websites. The former used WordPress, while the latter used Drupal. The irony was that the simpler website was using the more complex platform, so the act of going simpler probably was not before time. Alternatives to WordPress were being surveyed for the first of the pair, but none had quite the flexibility, pervasiveness and ease of use that WordPress offers.
There is another approach that has been gaining notice recently. One part of this is the use of Markdown for web publishing. This is a simple and distraction-free plain text format that can be transformed into something more readable. It sees usage in blogs hosted on GitHub, but also facilitates the generation of static websites. The clutter is absent for those who have no need of the Gutenberg Editor on WordPress.
With the content written in Markdown, it can be fed to a static website generator like Hugo. Using defined templates and fixed assets like CSS together with images and other static files, it can slot the content into HTML files very speedily since it is written in the Go programming language. Once you get acclimatised, there are no folder structures that cannot be used, so you get full flexibility in how you build out your website. Sitemaps and RSS feeds can be built at the same time, both using the same input as the HTML files.
In a nutshell, it automates what once needed manual effort used a code editor or a visual web page editor. The use of HTML snippets and layouts means that there is no necessity for hand-coding content, like there was at the start of the web. It also helps that Bootstrap can be built in using Node, so that gives a basis for any styling. Then, SCSS can take care of things, giving even more automation.
Given that there is no database involved in any of this, the required information has to be stored somewhere, and neither the Markdown content nor the layout files contain all that is needed. The main site configuration is defined in a single TOML file, and you can have a single one of these for every publishing destination; I have development and production servers, which makes this a very handy feature. Otherwise, every Markdown file needs a YAML header where titles, template references, publishing status and other similar information gets defined. The layouts then are linked to their components, and control logic and other advanced functionality can be added too.
Because static files are being created, it does mean that site searching and commenting, or contact pages cannot work like they would on a dynamic web platform. Often, external services are plugged in using JavaScript. One that I use for contact forms is Forminit. Then, Zapier has had its uses in using the RSS feed to tweet site updates on Twitter when new content gets added. Though I made different choices, Disqus can be used for comments and Algolia for site searching. Generally, though, you can find yourself needing to pay, particularly if you need to remove advertising or gain advanced features.
Some commenting service providers offer open source self-hosted options, but I found these difficult to set up and ended up not offering commenting at all. That was after I tried out Cactus Comments only to find that it was not discriminating between pages, so it showed the same comments everywhere. There are numerous alternatives like Remark42, Hyvor Talk, Commento, FastComments, Utterances, Isso, Mouthful, Muut and HyperComments but trying them all out was too time-consuming for what commenting was worth to me. It also explains why some static websites even send readers to Twitter if they have something to say, though I have not followed this way of working.
For searching, I added a JavaScript/JSON self-hosted component to the transport website, and it works well. However, it adds to the size of what a browser needs to download. That is not a major issue for desktop browsers, but the situation with mobile browsers is such that it has a sizeable effect. Testing with PageSpeed and Lighthouse highlighted this, even if I left things as they are. The solution works well in any case.
One thing that I have yet to work out is how to edit or add content while away from home. Editing files using an SSH connection is as much a possibility as setting up a Hugo publishing setup on a laptop. After that, there is the question of using a tablet or phone, since content management systems make everything web based. These are points that I have yet to explore.
As is natural with a code-based solution, there is a learning curve with Hugo. Reading a book provided some orientation, and looking on the web resolved many conundrums. There is good documentation on the project website, while forum discussions turn up on many a web search. Following any research, there was next to nothing that could not be done in some way.
Migration of content takes some forethought and took quite a bit of time, though there was an opportunity to carry some housekeeping as well. The history website was small, so copying and pasting sufficed. For the transport website, I used Python to convert what was on the database into Markdown files before refining the result. That provided some automation, but left a lot of work to be done afterwards.
The results were satisfactory, and I like the associated simplicity and efficiency. That Hugo works so fast means that it can handle large websites, so it is scalable. The new Markdown method for content production is not problematical so far apart from the need to make it more portable, and it helps that I found a setup that works for me. This also avoids any potential dealbreakers that continued development of publishing platforms like WordPress or Drupal could bring. For the former, I hope to remain with the Classic Editor indefinitely, but now have another option in case things go too far.
Improving a website contact form
On another website, I have had a contact form, but it was missing some functionality. For instance, it stored the input in files on a web server instead of emailing them. That was fixed more easily than expected using the PHP mail function. Even so, it remains useful to survey corresponding documentation on the W3Schools website.
The other changes affected the way the form looked to a visitor. There was a reset button, and that was removed on finding that such things are out of favour these days. Thinking again, there hardly was any need for it any way.
Newer additions that came with HTML5 had their place too. Including user hints using the placeholder attribute should add some user-friendliness, although I have avoided experimenting with browser-powered input validation for now. Use of the required attribute has its uses for telling a visitor that they have forgotten something, but I need to check how that is handled in CSS more thoroughly before I go with that since there are new :required, :optional, :valid and :invalid pseudoclasses that can be used to help.
It appears that there is much more to learn about setting up forms since I last checked. This is perhaps a hint that a few books need reading as part of catching with how things are done these days. There also is something new to learn.
Changing to web fonts
While you can add Windows fonts to Linux installations, I have found that their display can be flaky to say the least. Linux Mint and Ubuntu display them as sharp as I'd like, but I have struggled to get the same sort of results from Arch Linux, while I am not so sure about Fedora or openSUSE either.
This led me to explore web fonts for my websites, with Google Web Fonts meeting my needs through options like Open Sans and Arimo. There have been others with which I have dallied, such as Droid Sans, but these are the ones on which I have settled for now. Both are in use on this website now, and I added calls for them to the web page headers using the following code (lines are wrapping due to space constraints):
<link href="http://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,400,300,600,700" rel="stylesheet" type="text/css">
<link href='http://fonts.googleapis.com/css?family=Arimo:400,400italic,700,700italic' rel='stylesheet' type='text/css'>
With those lines in place, it then is a matter of updating font-family and font declarations in CSS style sheets with "Open Sans" or "Arimo" as needed, while keeping alternatives defined in case the Google font service goes down for whatever reason. A look at a development release of the WordPress Twenty Twelve theme caused me to come across Open Sans and I like it for its clean lines and Arimo, which was found by looking through the growing Google Web Fonts catalogue, is not far behind. Looking through that catalogue now causes for me a round of indecision since there is so much choice. For that reason, I think it's better to be open to the recommendations of others.
DePo Masthead
There is a place on WordPress.com where I share various odds and ends about public transport in the U.K. It's called On Trains and Buses, and I try not to go tinkering with the design side of things too much. You only can change the CSS and my previous experience of doing that with this edifice while it lived on there taught me not to expect too much even if there are sandbox themes for anyone to turn into something presentable, not that I really would want to go doing that in full view of everyone (doing if offline first and copying the CSS afterwards when it's done is my preferred way of going about it). Besides, I wanted to see how WordPress.com fares these days anyway.
While my public transport blog just been around for a little over a year, it's worn a few themes over that time, ranging from the minimalist The Journalist v1.9 and Vigilance through to Spring Reloaded. After the last of these, I am back to minimalist again with DePo Masthead, albeit with a spot of my own colouring to soften its feel a little. Though I must admit growing to like it, it came to my attention that it was a bespoke design from Derek Powazek that Automattic's Noel Jackson turned into reality. The result would appear that you cannot get it anywhere but from the WordPress.com Subversion theme repository. For those not versed in the little bit of Subversion action that is needed to get it, I did it for you and put it all into a zip file without making any changes to the original, hoping that it might make life easier for someone.
Some oddness with table cell display in Opera resolved

A while back, I reported a baffling problem with Opera managing to miss out an entry in the calendar widget on my hillwalking blog. After a bit of fiddling, I managed to track down the problem: setting position:relative in the CSS for hyperlink tags on my theme. While commenting out CSS declarations may seem a low technology way of finding problems like this, it still retains its place, as this little episode proves. Changing it to position:static for the hyperlinked numbers in the table resolved the issue, while I left the defaults as they were in case they had an adverse impact elsewhere.
If all this sounds rather too empirical in its approach, then I can only agree with you, yet a fix is a fix nonetheless. However, display:block is also set for the table entries, which means that may have a part to play too. Regardless of the trial and error feel to the solution, I could not find the problem documented anywhere, so I am sharing it here to help any others who encounter the same sort of weirdness.
Another way to control line breaks in (X)HTML
While you can use <br /> tags, there is another way to achieve similar results: the or non-breaking space entity. Put one of them between two words, and you stop them getting separated by a line break; I have been using this in the latest design tweaks that I made to my online photo gallery. Turning this on its head, if you see two words together acting without regard to normal wrapping conventions, then you can suspect that a non-breaking space could be a cause. There might be CSS options too, but their effectiveness in different browsers may limit their usefulness.