Init Commit

This commit is contained in:
2026-05-18 11:46:02 +02:00
commit 2de3502fbc
382 changed files with 19583 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
# Build and Release Folders
bin/
bin-debug/
bin-release/
[Oo]bj/ # FlashDevelop obj
[Bb]in/ # FlashDevelop bin
# Other files and folders
.settings/
# lock
*.lock
# Executables
*.swf
*.air
*.ipa
*.apk
# Hugo
public/
# Backup Files
*.markdown~
*.md~
# Apache
.htpasswd
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Build directories
public
dist
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
package-lock.json
bower_components
# Mac File System File...utterly useless to anyone but me.
.DS_Store
# AWS stuff for publishing static files
.aws-credentials.json
.awspublish*
+6
View File
@@ -0,0 +1,6 @@
[submodule "themes/devfest-theme-hugo"]
path = themes/devfest-theme-hugo
url = https://github.com/GDGToulouse/devfest-theme-hugo.git
[submodule "themes/adritian"]
path = themes/adritian
url = https://github.com/zetxek/adritian-free-hugo-theme.git
+5
View File
@@ -0,0 +1,5 @@
+++
date = '{{ .Date }}'
draft = true
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
+++
+179
View File
@@ -0,0 +1,179 @@
---
title: "Datenschutzerklärung"
date: 2025-12-11T10:00:00+01:00
layout: page
url: "/datenschutz/"
---
## 1. Datenschutz auf einen Blick
### Allgemeine Hinweise
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen Sie persönlich identifiziert werden können. Ausführliche Informationen zum Thema Datenschutz entnehmen Sie unserer unter diesem Text aufgeführten Datenschutzerklärung.
### Datenerfassung auf dieser Website
#### Wer ist verantwortlich für die Datenerfassung auf dieser Website?
Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten können Sie dem Abschnitt „Hinweis zur Verantwortlichen Stelle“ in dieser Datenschutzerklärung entnehmen.
#### Wie erfassen wir Ihre Daten?
Ihre Daten werden zum einen dadurch erhoben, dass Sie uns diese mitteilen. Hierbei kann es sich z.B. um Daten handeln, die Sie in ein Kontaktformular eingeben.
Andere Daten werden automatisch oder nach Ihrer Einwilligung beim Besuch der Website durch unsere IT-Systeme erfasst. Das sind vor allem technische Daten (z.B. Internetbrowser, Betriebssystem oder Uhrzeit des Seitenaufrufs). Die Erfassung dieser Daten erfolgt automatisch, sobald Sie diese Website betreten.
#### Wofür nutzen wir Ihre Daten?
Ein Teil der Daten wird erhoben, um eine fehlerfreie Bereitstellung der Website zu gewährleisten. Andere Daten können zur Analyse Ihres Nutzerverhaltens verwendet werden. Sofern über die Website Verträge geschlossen oder angebahnt werden können, werden die übermittelten Daten auch für Vertragsangebote, Bestellungen oder sonstige Auftragsanfragen verarbeitet.
#### Welche Rechte haben Sie bezüglich Ihrer Daten?
Sie haben jederzeit das Recht, unentgeltlich Auskunft über Herkunft, Empfänger und Zweck Ihrer gespeicherten personenbezogenen Daten zu erhalten. Sie haben außerdem ein Recht, die Berichtigung oder Löschung dieser Daten zu verlangen. Wenn Sie eine Einwilligung zur Datenverarbeitung erteilt haben, können Sie diese Einwilligung jederzeit für die Zukunft widerrufen. Außerdem haben Sie das Recht, unter bestimmten Umständen die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen. Des Weiteren steht Ihnen ein Beschwerderecht bei der zuständigen Aufsichtsbehörde zu.
Hierzu sowie zu weiteren Fragen zum Thema Datenschutz können Sie sich jederzeit an uns wenden.
## 2. Hosting
Wir hosten die Inhalte unserer Website bei folgendem Anbieter:
### Externes Hosting
Diese Website wird extern gehostet. Die personenbezogenen Daten, die auf dieser Website erfasst werden, werden auf den Servern des Hosters/der Hoster gespeichert. Hierbei kann es sich v.a. um IP-Adressen, Kontaktanfragen, Meta- und Kommunikationsdaten, Vertragsdaten, Kontaktdaten, Namen, Websitezugriffe und sonstige Daten, die über eine Website generiert werden, handeln.
Das externe Hosting erfolgt zum Zwecke der Vertragserfüllung gegenüber unseren potenziellen und bestehenden Kunden (Art. 6 Abs. 1 lit. b DSGVO) und im Interesse einer sicheren, schnellen und effizienten Bereitstellung unseres Online-Angebots durch einen professionellen Anbieter (Art. 6 Abs. 1 lit. f DSGVO). Sofern eine entsprechende Einwilligung abgefragt wurde, erfolgt die Verarbeitung ausschließlich auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO und § 25 Abs. 1 TDDDG, soweit die Einwilligung die Speicherung von Cookies oder den Zugriff auf Informationen im Endgerät des Nutzers (z.B. Device-Fingerprinting) im Sinne des TDDDG umfasst. Die Einwilligung ist jederzeit widerrufbar.
Unser(e) Hoster wird bzw. werden Ihre Daten nur insoweit verarbeiten, wie dies zur Erfüllung seiner Leistungspflichten erforderlich ist und unsere Weisungen in Bezug auf diese Daten befolgen.
Wir setzen folgende(n) Hoster ein:
**1blu GmbH**
Riedemannweg 60
D-13627 Berlin
## 3. Allgemeine Hinweise und Pflichtinformationen
### Datenschutz
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend den gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung.
Wenn Sie diese Website benutzen, werden verschiedene personenbezogene Daten erhoben. Personenbezogene Daten sind Daten, mit denen Sie persönlich identifiziert werden können. Die vorliegende Datenschutzerklärung erläutert, welche Daten wir erheben und wofür wir sie nutzen. Sie erläutert auch, wie und zu welchem Zweck das geschieht.
Wir weisen darauf hin, dass die Datenübertragung im Internet (z.B. bei der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser Schutz der Daten vor dem Zugriff durch Dritte ist nicht möglich.
### Hinweis zur verantwortlichen Stelle
Die verantwortliche Stelle für die Datenverarbeitung auf dieser Website ist:
**Lucas Hahmann**
Postfach 2002
Güterstr. 56
54295 Trier
Telefon: 017622852994
E-Mail: info@deepwatersurvival.de
Verantwortliche Stelle ist die natürliche oder juristische Person, die allein oder gemeinsam mit anderen über die Zwecke und Mittel der Verarbeitung von personenbezogenen Daten (z.B. Namen, E-Mail-Adressen o. Ä.) entscheidet.
### Speicherdauer
Soweit innerhalb dieser Datenschutzerklärung keine speziellere Speicherdauer genannt wurde, verbleiben Ihre personenbezogenen Daten bei uns, bis der Zweck für die Datenverarbeitung entfällt. Wenn Sie ein berechtigtes Löschersuchen geltend machen oder eine Einwilligung zur Datenverarbeitung widerrufen, werden Ihre Daten gelöscht, sofern wir keine anderen rechtlich zulässigen Gründe für die Speicherung Ihrer personenbezogenen Daten haben (z.B. steuer- oder handelsrechtliche Aufbewahrungsfristen); im letztgenannten Fall erfolgt die Löschung nach Fortfall dieser Gründe.
### Allgemeine Hinweise zu den Rechtsgrundlagen der Datenverarbeitung
Sofern Sie in die Datenverarbeitung eingewilligt haben, verarbeiten wir Ihre personenbezogenen Daten auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO bzw. Art. 9 Abs. 2 lit. a DSGVO, sofern besondere Datenkategorien nach Art. 9 Abs. 1 DSGVO verarbeitet werden. Im Falle einer ausdrücklichen Einwilligung in die Übertragung personenbezogener Daten in Drittstaaten erfolgt die Datenverarbeitung außerdem auf Grundlage von Art. 49 Abs. 1 lit. a DSGVO. Sofern Sie in die Speicherung von Cookies oder in den Zugriff auf Informationen in Ihr Endgerät (z.B. via Device-Fingerprinting) eingewilligt haben, erfolgt die Datenverarbeitung zusätzlich auf Grundlage von § 25 Abs. 1 TDDDG. Die Einwilligung ist jederzeit widerrufbar. Sind Ihre Daten zur Vertragserfüllung oder zur Durchführung vorvertraglicher Maßnahmen erforderlich, verarbeiten wir Ihre Daten auf Grundlage des Art. 6 Abs. 1 lit. b DSGVO. Des Weiteren verarbeiten wir Ihre Daten, sofern diese zur Erfüllung einer rechtlichen Verpflichtung erforderlich sind auf Grundlage von Art. 6 Abs. 1 lit. c DSGVO. Die Datenverarbeitung kann ferner auf Grundlage unseres berechtigten Interesses nach Art. 6 Abs. 1 lit. f DSGVO erfolgen. Über die jeweils im Einzelfall einschlägigen Rechtsgrundlagen wird in den folgenden Absätzen dieser Datenschutzerklärung informiert.
### Empfänger von personenbezogenen Daten
Im Rahmen unserer Geschäftstätigkeit arbeiten wir mit verschiedenen externen Stellen zusammen. Dabei ist teilweise auch eine Übermittlung von personenbezogenen Daten an diese externen Stellen erforderlich. Wir geben personenbezogene Daten nur dann an externe Stellen weiter, wenn dies im Rahmen einer Vertragserfüllung erforderlich ist, wenn wir gesetzlich hierzu verpflichtet sind (z.B. Weitergabe von Daten an Steuerbehörden), wenn wir ein berechtigtes Interesse nach Art. 6 Abs. 1 lit. f DSGVO an der Weitergabe haben oder wenn eine sonstige Rechtsgrundlage die Datenweitergabe erlaubt. Beim Einsatz von Auftragsverarbeitern geben wir personenbezogene Daten unserer Kunden nur auf Grundlage eines gültigen Vertrags über Auftragsverarbeitung weiter. Im Falle einer gemeinsamen Verarbeitung wird ein Vertrag über gemeinsame Verarbeitung geschlossen.
### Widerruf Ihrer Einwilligung zur Datenverarbeitung
Viele Datenverarbeitungsvorgänge sind nur mit Ihrer ausdrücklichen Einwilligung möglich. Sie können eine bereits erteilte Einwilligung jederzeit widerrufen. Die Rechtmäßigkeit der bis zum Widerruf erfolgten Datenverarbeitung bleibt vom Widerruf unberührt.
### Widerspruchsrecht gegen die Datenerhebung in besonderen Fällen sowie gegen Direktwerbung (Art. 21 DSGVO)
Wenn die Datenverarbeitung auf Grundlage von Art. 6 Abs. 1 lit. e oder f DSGVO erfolgt, haben Sie jederzeit das Recht, aus Gründen, die sich aus Ihrer besonderen Situation ergeben, gegen die Verarbeitung Ihrer personenbezogenen Daten Widerspruch einzulegen; dies gilt auch für ein auf diese Bestimmungen gestütztes Profiling. Die jeweilige Rechtsgrundlage, auf der eine Verarbeitung beruht, entnehmen Sie dieser Datenschutzerklärung. Wenn Sie Widerspruch einlegen, werden wir Ihre betroffenen personenbezogenen Daten nicht mehr verarbeiten, es sei denn, wir können zwingende schutzwürdige Gründe für die Verarbeitung nachweisen, die Ihre Interessen, Rechte und Freiheiten überwiegen oder die Verarbeitung dient der Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen (Widerspruch nach Art. 21 Abs. 1 DSGVO).
Werden Ihre personenbezogenen Daten verarbeitet, um Direktwerbung zu betreiben, so haben Sie das Recht, jederzeit Widerspruch gegen die Verarbeitung Sie betreffender personenbezogener Daten zum Zwecke derartiger Werbung einzulegen; dies gilt auch für das Profiling, soweit es mit solcher Direktwerbung in Verbindung steht. Wenn Sie widersprechen, werden Ihre personenbezogenen Daten anschließend nicht mehr zum Zwecke der Direktwerbung verwendet (Widerspruch nach Art. 21 Abs. 2 DSGVO).
### Beschwerderecht bei der zuständigen Aufsichtsbehörde
Im Falle von Verstößen gegen die DSGVO steht den Betroffenen ein Beschwerderecht bei einer Aufsichtsbehörde, insbesondere in dem Mitgliedstaat ihres gewöhnlichen Aufenthalts, ihres Arbeitsplatzes oder des Orts des mutmaßlichen Verstoßes zu. Das Beschwerderecht besteht unbeschadet anderweitiger verwaltungsrechtlicher oder gerichtlicher Rechtsbehelfe.
### Recht auf Datenübertragbarkeit
Sie haben das Recht, Daten, die wir auf Grundlage Ihrer Einwilligung oder in Erfüllung eines Vertrags automatisiert verarbeiten, an sich oder an einen Dritten in einem gängigen, maschinenlesbaren Format aushändigen zu lassen. Sofern Sie die direkte Übertragung der Daten an einen anderen Verantwortlichen verlangen, erfolgt dies nur, soweit es technisch machbar ist.
### Auskunft, Berichtigung und Löschung
Sie haben im Rahmen der geltenden gesetzlichen Bestimmungen jederzeit das Recht auf unentgeltliche Auskunft über Ihre gespeicherten personenbezogenen Daten, deren Herkunft und Empfänger und den Zweck der Datenverarbeitung und ggf. ein Recht auf Berichtigung oder Löschung dieser Daten. Hierzu sowie zu weiteren Fragen zum Thema personenbezogene Daten können Sie sich jederzeit an uns wenden.
### Recht auf Einschränkung der Verarbeitung
Sie haben das Recht, die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen. Hierzu können Sie sich jederzeit an uns wenden. Das Recht auf Einschränkung der Verarbeitung besteht in folgenden Fällen:
- Wenn Sie die Richtigkeit Ihrer bei uns gespeicherten personenbezogenen Daten bestreiten, benötigen wir in der Regel Zeit, um dies zu überprüfen. Für die Dauer der Prüfung haben Sie das Recht, die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
- Wenn die Verarbeitung Ihrer personenbezogenen Daten unrechtmäßig geschah/geschieht, können Sie statt der Löschung die Einschränkung der Datenverarbeitung verlangen.
- Wenn wir Ihre personenbezogenen Daten nicht mehr benötigen, Sie sie jedoch zur Ausübung, Verteidigung oder Geltendmachung von Rechtsansprüchen benötigen, haben Sie das Recht, statt der Löschung die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
- Wenn Sie einen Widerspruch nach Art. 21 Abs. 1 DSGVO eingelegt haben, muss eine Abwägung zwischen Ihren und unseren Interessen vorgenommen werden. Solange noch nicht feststeht, wessen Interessen überwiegen, haben Sie das Recht, die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
Wenn Sie die Verarbeitung Ihrer personenbezogenen Daten eingeschränkt haben, dürfen diese Daten von ihrer Speicherung abgesehen nur mit Ihrer Einwilligung oder zur Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen oder zum Schutz der Rechte einer anderen natürlichen oder juristischen Person oder aus Gründen eines wichtigen öffentlichen Interesses der Europäischen Union oder eines Mitgliedstaats verarbeitet werden.
### SSL- bzw. TLS-Verschlüsselung
Diese Seite nutzt aus Sicherheitsgründen und zum Schutz der Übertragung vertraulicher Inhalte, wie zum Beispiel Bestellungen oder Anfragen, die Sie an uns als Seitenbetreiber senden, eine SSL- bzw. TLS-Verschlüsselung. Eine verschlüsselte Verbindung erkennen Sie daran, dass die Adresszeile des Browsers von „http://“ auf „https://“ wechselt und an dem Schloss-Symbol in Ihrer Browserzeile.
Wenn die SSL- bzw. TLS-Verschlüsselung aktiviert ist, können die Daten, die Sie an uns übermitteln, nicht von Dritten mitgelesen werden.
### Widerspruch gegen Werbe-E-Mails
Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-E-Mails, vor.
## 4. Datenerfassung auf dieser Website
### Cookies
Unsere Internetseiten verwenden so genannte „Cookies“. Cookies sind kleine Datenpakete und richten auf Ihrem Endgerät keinen Schaden an. Sie werden entweder vorübergehend für die Dauer einer Sitzung (Session-Cookies) oder dauerhaft (permanente Cookies) auf Ihrem Endgerät gespeichert. Session-Cookies werden nach Ende Ihres Besuchs automatisch gelöscht. Permanente Cookies bleiben auf Ihrem Endgerät gespeichert, bis Sie diese selbst löschen oder eine automatische Löschung durch Ihren Webbrowser erfolgt.
Cookies können von uns (First-Party-Cookies) oder von Drittunternehmen stammen (sog. Third-Party-Cookies). Third-Party-Cookies ermöglichen die Einbindung bestimmter Dienstleistungen von Drittunternehmen innerhalb von Webseiten (z.B. Cookies zur Abwicklung von Zahlungsdienstleistungen).
Cookies haben verschiedene Funktionen. Zahlreiche Cookies sind technisch notwendig, da bestimmte Webseitenfunktionen ohne diese nicht funktionieren würden (z.B. die Warenkorbfunktion oder die Anzeige von Videos). Andere Cookies können zur Auswertung des Nutzerverhaltens oder zu Werbezwecken verwendet werden.
Cookies, die zur Durchführung des elektronischen Kommunikationsvorgangs, zur Bereitstellung bestimmter, von Ihnen erwünschter Funktionen (z.B. für die Warenkorbfunktion) oder zur Optimierung der Website (z.B. Cookies zur Messung des Webpublikums) erforderlich sind (notwendige Cookies), werden auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO gespeichert, sofern keine andere Rechtsgrundlage angegeben wird. Der Websitebetreiber hat ein berechtigtes Interesse an der Speicherung von notwendigen Cookies zur technisch fehlerfreien und optimierten Bereitstellung seiner Dienste. Sofern eine Einwilligung zur Speicherung von Cookies und vergleichbaren Wiedererkennungstechnologien abgefragt wurde, erfolgt die Verarbeitung ausschließlich auf Grundlage dieser Einwilligung (Art. 6 Abs. 1 lit. a DSGVO und § 25 Abs. 1 TDDDG); die Einwilligung ist jederzeit widerrufbar.
Sie können Ihren Browser so einstellen, dass Sie über das Setzen von Cookies informiert werden und Cookies nur im Einzelfall erlauben, die Annahme von Cookies für bestimmte Fälle oder generell ausschließen sowie das automatische Löschen der Cookies beim Schließen des Browsers aktivieren. Bei der Deaktivierung von Cookies kann die Funktionalität dieser Website eingeschränkt sein.
Welche Cookies und Dienste auf dieser Website eingesetzt werden, können Sie dieser Datenschutzerklärung entnehmen.
### Anfrage per E-Mail, Telefon oder Telefax
Wenn Sie uns per E-Mail, Telefon oder Telefax kontaktieren, wird Ihre Anfrage inklusive aller daraus hervorgehenden personenbezogenen Daten (Name, Anfrage) zum Zwecke der Bearbeitung Ihres Anliegens bei uns gespeichert und verarbeitet. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.
Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre Anfrage mit der Erfüllung eines Vertrags zusammenhängt oder zur Durchführung vorvertraglicher Maßnahmen erforderlich ist. In allen übrigen Fällen beruht die Verarbeitung auf unserem berechtigten Interesse an der effektiven Bearbeitung der an uns gerichteten Anfragen (Art. 6 Abs. 1 lit. f DSGVO) oder auf Ihrer Einwilligung (Art. 6 Abs. 1 lit. a DSGVO) sofern diese abgefragt wurde; die Einwilligung ist jederzeit widerrufbar.
Die von Ihnen an uns per Kontaktanfragen übersandten Daten verbleiben bei uns, bis Sie uns zur Löschung auffordern, Ihre Einwilligung zur Speicherung widerrufen oder der Zweck für die Datenspeicherung entfällt (z.B. nach abgeschlossener Bearbeitung Ihres Anliegens). Zwingende gesetzliche Bestimmungen insbesondere gesetzliche Aufbewahrungsfristen bleiben unberührt.
## 5. Plugins und Tools
### Google Fonts
Diese Seite nutzt zur einheitlichen Darstellung von Schriftarten so genannte Google Fonts, die von Google bereitgestellt werden. Beim Aufruf einer Seite lädt Ihr Browser die benötigten Fonts in ihren Browsercache, um Texte und Schriftarten korrekt anzuzeigen.
Zu diesem Zweck muss der von Ihnen verwendete Browser Verbindung zu den Servern von Google aufnehmen. Hierdurch erlangt Google Kenntnis darüber, dass über Ihre IP-Adresse diese Website aufgerufen wurde. Die Nutzung von Google Fonts erfolgt auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO. Der Websitebetreiber hat ein berechtigtes Interesse an der einheitlichen Darstellung des Schriftbildes auf seiner Website. Sofern eine entsprechende Einwilligung abgefragt wurde, erfolgt die Verarbeitung ausschließlich auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO und § 25 Abs. 1 TDDDG, soweit die Einwilligung die Speicherung von Cookies oder den Zugriff auf Informationen im Endgerät des Nutzers (z.B. Device-Fingerprinting) im Sinne des TDDDG umfasst. Die Einwilligung ist jederzeit widerrufbar.
Wenn Ihr Browser Google Fonts nicht unterstützt, wird eine Standardschrift von Ihrem Computer genutzt.
Weitere Informationen zu Google Fonts finden Sie unter [https://developers.google.com/fonts/faq](https://developers.google.com/fonts/faq) und in der Datenschutzerklärung von Google: [https://policies.google.com/privacy?hl=de](https://policies.google.com/privacy?hl=de
Das Unternehmen verfügt über eine Zertifizierung nach dem „EU-US Data Privacy Framework“ (DPF). Der
DPF ist ein Übereinkommen zwischen der Europäischen Union und den USA, der die Einhaltung
europäischer Datenschutzstandards bei Datenverarbeitungen in den USA gewährleisten soll. Jedes nach
dem DPF zertifizierte Unternehmen verpflichtet sich, diese Datenschutzstandards einzuhalten. Weitere
Informationen hierzu erhalten Sie vom Anbieter unter folgendem Link:
https://www.dataprivacyframework.gov/participant/5780.
### Spotify
Auf dieser Website sind Funktionen des Musik-Dienstes Spotify eingebunden. Anbieter ist die Spotify AB,
Birger Jarlsgatan 61, 113 56 Stockholm in Schweden. Die Spotify Plugins erkennen Sie an dem grünen Logo
auf dieser Website. Eine Übersicht über die Spotify-Plugins finden Sie unter:
https://developer.spotify.com.
Dadurch kann beim Besuch dieser Website über das Plugin eine direkte Verbindung zwischen Ihrem
Browser und dem Spotify-Server hergestellt werden. Spotify erhält dadurch die Information, dass Sie mit
Ihrer IP-Adresse diese Website besucht haben. Wenn Sie den Spotify Button anklicken, während Sie in
Ihrem Spotify-Account eingeloggt sind, können Sie die Inhalte dieser Website auf Ihrem Spotify Profil
verlinken. Dadurch kann Spotify den Besuch dieser Website Ihrem Benutzerkonto zuordnen.
Wir weisen darauf hin, dass bei der Nutzung von Spotify Cookies von Google Analytics eingesetzt werden,
sodass Ihre Nutzungsdaten bei der Nutzung von Spotify auch an Google weitergegeben werden können.
Google Analytics ist ein Tool des Google-Konzerns zur Analyse des Nutzerverhaltens mit Sitz in den USA.
Für diese Einbindung ist allein Spotify verantwortlich. Wir als Websitebetreiber haben auf diese
Verarbeitung keinen Einfluss.
Die Speicherung und Analyse der Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO. Der
Websitebetreiber hat ein berechtigtes Interesse an der ansprechenden akustischen Ausgestaltung seiner
Website. Sofern eine entsprechende Einwilligung abgefragt wurde, erfolgt die Verarbeitung ausschließlich
auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO und § 25 Abs. 1 TDDDG, soweit die Einwilligung die
Speicherung von Cookies oder den Zugriff auf Informationen im Endgerät des Nutzers (z. B. Device-
Fingerprinting) im Sinne des TDDDG umfasst. Die Einwilligung ist jederzeit widerrufbar.
Weitere Informationen hierzu finden Sie in der Datenschutzerklärung von Spotify:
https://www.spotify.com/de/legal/privacy-policy/.
Wenn Sie nicht wünschen, dass Spotify den Besuch dieser Website Ihrem Spotify-Nutzerkonto zuordnen
kann, loggen Sie sich bitte aus Ihrem Spotify-Benutzerkonto aus.
+30
View File
@@ -0,0 +1,30 @@
---
title: "Impressum"
date: 2025-12-08T10:00:00+01:00
layout: page
url: "/impressum/"
---
Lucas Hahmann
Postfach 2002
Güterstr. 56
54295 Trier
E-Mail: <p>info [at] deepwatersurvival [dot] de</p>
## Disclaimer rechtliche Hinweise
### § 1 Warnhinweis zu Inhalten
Die kostenlosen und frei zugänglichen Inhalte dieser Webseite wurden mit größtmöglicher Sorgfalt erstellt. Der Anbieter dieser Webseite übernimmt jedoch keine Gewähr für die Richtigkeit und Aktualität der bereitgestellten kostenlosen und frei zugänglichen journalistischen Ratgeber und Nachrichten. Namentlich gekennzeichnete Beiträge geben die Meinung des jeweiligen Autors und nicht immer die Meinung des Anbieters wieder. Allein durch den Aufruf der kostenlosen und frei zugänglichen Inhalte kommt keinerlei Vertragsverhältnis zwischen dem Nutzer und dem Anbieter zustande, insoweit fehlt es am Rechtsbindungswillen des Anbieters.
### § 2 Externe Links
Diese Website enthält Verknüpfungen zu Websites Dritter ("externe Links"). Diese Websites unterliegen der Haftung der jeweiligen Betreiber. Der Anbieter hat bei der erstmaligen Verknüpfung der externen Links die fremden Inhalte daraufhin überprüft, ob etwaige Rechtsverstöße bestehen. Zu dem Zeitpunkt waren keine Rechtsverstöße ersichtlich. Der Anbieter hat keinerlei Einfluss auf die aktuelle und zukünftige Gestaltung und auf die Inhalte der verknüpften Seiten. Das Setzen von externen Links bedeutet nicht, dass sich der Anbieter die hinter dem Verweis oder Link liegenden Inhalte zu Eigen macht. Eine ständige Kontrolle der externen Links ist für den Anbieter ohne konkrete Hinweise auf Rechtsverstöße nicht zumutbar. Bei Kenntnis von Rechtsverstößen werden jedoch derartige externe Links unverzüglich gelöscht.
### § 3 Urheber- und Leistungsschutzrechte
Die auf dieser Website veröffentlichten Inhalte unterliegen dem deutschen Urheber- und Leistungsschutzrecht. Jede vom deutschen Urheber- und Leistungsschutzrecht nicht zugelassene Verwertung bedarf der vorherigen schriftlichen Zustimmung des Anbieters oder jeweiligen Rechteinhabers. Dies gilt insbesondere für Vervielfältigung, Bearbeitung, Übersetzung, Einspeicherung, Verarbeitung bzw. Wiedergabe von Inhalten in Datenbanken oder anderen elektronischen Medien und Systemen. Inhalte und Rechte Dritter sind dabei als solche gekennzeichnet. Die unerlaubte Vervielfältigung oder Weitergabe einzelner Inhalte oder kompletter Seiten ist nicht gestattet und strafbar. Lediglich die Herstellung von Kopien und Downloads für den persönlichen, privaten und nicht kommerziellen Gebrauch ist erlaubt.
Die Darstellung dieser Website in fremden Frames ist nur mit schriftlicher Erlaubnis zulässig.
### § 4 Besondere Nutzungsbedingungen
Soweit besondere Bedingungen für einzelne Nutzungen dieser Website von den vorgenannten Paragraphen abweichen, wird an entsprechender Stelle ausdrücklich darauf hingewiesen. In diesem Falle gelten im jeweiligen Einzelfall die besonderen Nutzungsbedingungen.
+5
View File
@@ -0,0 +1,5 @@
---
title: "Home"
---
Willkommen auf der offiziellen Seite von **Deepwater Survival** dreckiger, lauter Punk Rock.
```
+103
View File
@@ -0,0 +1,103 @@
+++
title = ""
menu = "main"
url = "/about/"
+++
<section id="team">
<h1 style="text-align:center;">Bandmitglieder</h1>
<!-- Mitglied 1 -->
<div class="team-row">
<div class="team-text">
<h2>Olly</h2>
<p>Lead Gitarrist und Sänger</p>
</div>
<div class="team-image">
<img src="/img/about/Olly_compressed.jpg" alt="Olly">
</div>
</div>
<!-- Mitglied 2 -->
<div class="team-row">
<div class="team-image">
<img src="/img/about/Lucas_compressed.jpg" alt="Lucas">
</div>
<div class="team-text">
<h2>Lucas</h2>
<p>Rhytmusgitarrist</p>
</div>
</div>
<!-- Mitglied 3 -->
<div class="team-row">
<div class="team-text">
<h2>Ahrens</h2>
<p>Schlagzeuger</p>
</div>
<div class="team-image">
<img src="/img/about/Ahrens_compressed.jpg" alt="John Doe">
</div>
</div>
<!-- Mitglied 4 -->
<div class="team-row">
<div class="team-image">
<img src="/img/about/Felix_compressed.jpg" alt="John Doe">
</div>
<div class="team-text">
<h2>Felix</h2>
<p>On the Bass</p>
</div>
</div>
<div id="Gruppenfoto">
<img src="/img/about/Gruppenbild_compressed.jpg" class="responsive-img" alt="Gruppenfoto" width="1200" height="800">
</div>
</section>
<style>
#team {
max-width: 1000px;
margin: 0 auto;
padding: 2rem;
}
.team-row {
display: flex;
align-items: center;
justify-content: space-between;
margin: 2rem 0;
gap: 2rem;
}
.team-text {
flex: 1;
}
.team-image img {
width: 300px;
height: auto;
border-radius: 10px;
}
.responsive-img {
max-width: 100%;
height: auto;
display: block;
margin: 2rem auto 0 auto; /* zentriert auf Mobil */
}
@media (max-width: 768px) {
.team-row {
flex-direction: column-reverse;
text-align: center;
}
.team-image img {
width: 80%;
margin: 0 auto;
}
}
</style>
+7
View File
@@ -0,0 +1,7 @@
---
title: "Galerie"
images:
- "https://picsum.photos/seed/picsum/200/300"
- "https://picsum.photos/200/300?grayscale"
- "https://picsum.photos/200/300/?blur"
---
+5
View File
@@ -0,0 +1,5 @@
---
title: "Musik"
---
Unsere Releases, Singles und Alben.
```
+6
View File
@@ -0,0 +1,6 @@
---
title: "JUKUFE Light"
date: 2022-08-22
location: Skatehalle Trier
poster: /img/shows/2022_Jukufe_light_compressed.jpg
---
+6
View File
@@ -0,0 +1,6 @@
---
title: "Konz wird Laut"
date: 2022-07-09
location: Haus der Jugend Konz
poster: /img/shows/2022_Konz_wird_laut_compressed.jpg
---
+6
View File
@@ -0,0 +1,6 @@
---
title: "Rock im Gehege"
date: 2022-07-22
location: Piesbach
poster: /img/shows/2022_Rock_im_Gehege_compressed.jpg
---
+6
View File
@@ -0,0 +1,6 @@
---
title: "Tefftival"
date: 2022-12-23
location: Tufa
poster: /img/shows/2022_Tefftival_compressed.jpg
---
+6
View File
@@ -0,0 +1,6 @@
---
title: "Moselrock"
date: 2023-05-27
location: Wasserliesch
poster: /img/shows/2023_Moselrock_compressed.jpg
---
+6
View File
@@ -0,0 +1,6 @@
---
title: "Rock für Ruanda"
date: 2024-07-22
location: Butzweiler
poster: /img/shows/2024_Rock_fuer_Ruanda_compressed.png
---
+6
View File
@@ -0,0 +1,6 @@
---
title: "Rock im Gehege"
date: 2024-12-11
location: Piesbach
poster: /img/shows/2024_Rock_im_Gehege_compressed.jpg
---
+6
View File
@@ -0,0 +1,6 @@
---
title: "Bullenhitze"
date: 2025-08-30
location: Salmtal
poster: /img/shows/2025_Bullenhitze.jpg
---
+4
View File
@@ -0,0 +1,4 @@
---
title: "Shows"
---
Hier findest du alle kommenden Live-Termine.
+9
View File
@@ -0,0 +1,9 @@
title = "Deepwater Survival"
baseURL = "https://www.deepwatersurvival.de/"
theme = ""
languageCode = "de"
[pagination]
pagerSize = 10
[markup.goldmark.renderer]
unsafe = true # Allow HTML in md files
+19
View File
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>{{ .Title }} | Deepwater Survival</title>
<link rel="stylesheet" href="/css/style.css">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap" rel="stylesheet">
</head>
<body>
{{ partial "header.html" . }}
<main>
{{ block "main" . }}{{ end }}
<div class="spacer"></div>
</main>
{{ partial "footer.html" . }}
</body>
</html>
+8
View File
@@ -0,0 +1,8 @@
{{ define "main" }}
<h1>{{ .Title }}</h1>
<ul>
{{ range .Pages }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}
+6
View File
@@ -0,0 +1,6 @@
{{ define "main" }}
<article>
<h1>{{ .Title }}</h1>
{{ .Content }}
</article>
{{ end }}
+15
View File
@@ -0,0 +1,15 @@
{{ define "main" }}
<link rel="stylesheet" href="/css/carousel.css">
<script defer src="/js/carousel.js"></script>
<section class="carousel">
<div class="carousel-inner">
{{ range $index, $img := .Params.images }}
<div class="carousel-item {{ if eq $index 0 }}active{{ end }}">
<img src="{{ $img }}" alt="Slide {{ add $index 1 }}">
</div>
{{ end }}
</div>
<button class="prev">&#10094;</button>
<button class="next">&#10095;</button>
</section>
{{ end }}
+27
View File
@@ -0,0 +1,27 @@
{{ define "main" }}
<section>
<link rel="stylesheet" href="/css/index.css">
<div id="main_image">
<img src="/img/AlbumReleaseParty_Stage_compressed.jpg" alt="" width="1000" height="667">
</div>
<section style="padding: 2rem; max-width: 800px; margin: auto; line-height: 1.6; ">
<h1 style="font-size: 2rem; margin-bottom: 1rem; text-align: center;">Deepwater Survival Frischer Wind aus der deutschen Punk-Szene</h1>
<p>Mit energiegeladenem Punkrock, ehrlichen Texten und unverblümter Direktheit meldet sich <strong>Deepwater Survival</strong> zurück. Die Band aus Trier besticht durch einen Sound, der roh, mitreißend und ehrlich ist und schafft es damit, sowohl alteingesessene Punkfans als auch neue Hörer:innen zu begeistern.</p>
<p>Bevor das Debütalbum <strong>„Ocean Tales“</strong> 2025 erschien, veröffentlichte die Band bereits eine Reihe kraftvoller Singles: <em>„Chirus“ (2023), „Let It Go“ (2024) sowie „Addiction“ und „My Own Home“</em>. Diese Songs, die fast unverändert auf dem Album zu finden sind, geben einen klaren Einblick in die musikalische und emotionale Bandbreite von Deepwater Survival.</p>
<p>Die Texte greifen Themen wie Identität, Zugehörigkeit, Abschied und Neuanfang auf universelle Inhalte, die in der Hektik unserer Zeit für viele relevant sind. Die Musik von Deepwater Survival lädt dazu ein, laut mitzusingen, nachzudenken und gemeinsam mit der Band ein Stück Erinnerung und Hoffnung zu teilen.</p>
<p><strong>Deepwater Survival</strong> steht für Punk mit Haltung klar, ungeschönt, direkt. Für alle, die Musik nicht nur hören, sondern fühlen wollen.</p>
</section>
<div>
<h2 style="text-align: center;">Intresse geweckt? Dann gerne hier melden: info@deepwatersurvival.de</h2>
</div>
<div id="spotify_link">
<iframe data-testid="embed-iframe" style="border-radius:12px" src="https://open.spotify.com/embed/artist/2TQ9HNBTNCmUthpg9ippHs?utm_source=generator&theme=0" width="40%" height="352" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
</div>
</section>
{{ end }}
+18
View File
@@ -0,0 +1,18 @@
<link rel="stylesheet" href="/css/footer.css">
<footer id="site_footer">
<div class="footer-container">
<div class="footer-left">
<p>© 2025 Deepwater Survival</p>
<p>
<a href="/impressum/" target="_self" rel="noopener">Impressum</a> |
<a href="/datenschutz/" target="_self" rel="noopener">Datenschutzerklärung</a>
</p>
</div>
<div class="footer-right">
<a href="https://open.spotify.com/artist/2TQ9HNBTNCmUthpg9ippHs" target="_blank" rel="noopener">Spotify</a>
<a href="https://www.instagram.com/deepwatersurvival/" target="_blank" rel="noopener">Instagram</a>
<a href="https://www.youtube.com/@deepwatersurvival730" target="_blank" rel="noopener">YouTube</a>
</div>
</div>
</footer>
+14
View File
@@ -0,0 +1,14 @@
<header>
<link rel="stylesheet" href="/css/header.css">
<div id="logo">
<img src="/img/Logo_compressed.png" alt="" height="218" width="600">
</div>
<nav id="main_navigation">
<a href="/">Home</a>
<a href="/about/">Über uns</a>
<a href="/shows/">Shows</a>
<a href="/music/">Musik</a>
</nav>
</header>
+59
View File
@@ -0,0 +1,59 @@
{{ define "main" }}
<h1>{{ .Title }}</h1>
<ul>
{{ range .Pages }}
<div id="shows">
<div class="shows-row">
<div id="shows-image">
<img src="{{ .Params.poster}}" width="300" height="300">
</div>
<div class="shows-text">
<strong>{{ .Params.date.Format "2006-01-02" }}</strong> {{ .Params.location }} {{ .Title }}
</div>
</div>
</div>
{{ end }}
</ul>
<style>
#shows {
max-width: 1000px;
margin: 0 auto;
padding: 2rem;
}
.shows-row {
display: flex;
align-items: center;
justify-content: space-between;
margin: 2rem 0;
gap: 2rem;
}
.shows-text {
flex: 1;
}
.shows-image img {
width: 300px;
height: auto;
border-radius: 10px;
}
@media (max-width: 768px) {
.shows-row {
flex-direction: column-reverse;
text-align: center;
}
.shows-image img {
width: 80%;
margin: 0 auto;
}
}
</style>
{{ end }}
+43
View File
@@ -0,0 +1,43 @@
.carousel {
position: relative;
/*max-width: 1000px; */
margin: auto;
overflow: hidden;
}
.carousel-inner {
display: flex;
transition: transform 0.5s ease;
}
.carousel-item {
min-width: 100%;
box-sizing: border-box;
}
.carousel-item img {
width: 100%;
display: block;
border-radius: 8px;
}
button.prev,
button.next {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0,0,0,0.5);
color: #fff;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 50%;
font-size: 2rem;
}
button.prev { left: 10px; }
button.next { right: 10px; }
button:hover {
background-color: rgba(0,0,0,0.8);
}
+59
View File
@@ -0,0 +1,59 @@
/* Footer Grundlayout */
#site_footer {
background-color: #111;
color: #eee;
padding: 30px 20px;
font-family: 'Montserrat', sans-serif;
border-top: 2px solid #eee;
}
#site_footer a {
color: #eee;
text-decoration: none;
margin-left: 15px;
font-weight: bold;
transition: color 0.3s;
}
#site_footer a:hover {
color: #fff;
}
.footer-container {
display: flex;
justify-content: space-between;
align-items: flex-start;
max-width: 1000px;
margin: auto;
flex-wrap: wrap;
}
.footer-left p {
margin: 5px 0;
font-size: 0.95rem;
}
.footer-right {
margin-top: 10px;
}
.footer-right a:first-child {
margin-left: 0;
}
/* Mobile Responsive */
@media (max-width: 600px) {
.footer-container {
flex-direction: column;
align-items: center;
text-align: center;
}
.footer-right {
margin-top: 15px;
}
#site_footer a {
margin: 0 10px;
}
}
+25
View File
@@ -0,0 +1,25 @@
#logo {
display: flex;
align-items: center;
justify-content: center;
}
#main_navigation {
display: flex; /* Container wird Flexbox */
align-items: center;
justify-content: center;
gap: 40px; /* Abstand zwischen den Links */
}
#main_navigation a {
text-decoration: none !important;
color: #eee;
cursor: pointer;
transition: color 0.3s;
font-size: 2rem;
}
#main_navigation a:hover {
text-decoration: underline; /* nur beim Überfahren */
color: #fff;
}
+29
View File
@@ -0,0 +1,29 @@
#main_image {
display: flex;
align-items: center;
justify-content: center;
}
/* Responsive Schriftgrößen */
@media (max-width: 768px) {
section {
padding: 1rem;
}
h1 {
font-size: 1.5rem;
}
}
@media (max-width: 480px) {
h1 {
font-size: 1.25rem;
}
p {
font-size: 0.95rem;
}
}
#spotify_link {
display: flex;
align-items: center;
justify-content: center;
}
+14
View File
@@ -0,0 +1,14 @@
body {
font-family: 'Montserrat', sans-serif;
background: #111;
color: #eee;
margin: 20;
}
h1, h2, h3 {
font-family: 'Oswald', sans-serif;
}
.spacer {
height: 30px; /* amount of vertical space */
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+17
View File
@@ -0,0 +1,17 @@
const carousel = document.querySelector('.carousel-inner');
const slides = document.querySelectorAll('.carousel-item');
let index = 0;
document.querySelector('.next').addEventListener('click', () => {
index = (index + 1) % slides.length;
updateCarousel();
});
document.querySelector('.prev').addEventListener('click', () => {
index = (index - 1 + slides.length) % slides.length;
updateCarousel();
});
function updateCarousel() {
carousel.style.transform = `translateX(-${index * 100}%)`;
}
+13
View File
@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [zetxek] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+40
View File
@@ -0,0 +1,40 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: zetxek
---
**Describe the bug**
A clear and concise description of what the bug is.
What browsers did you test?
Do you have a public URL to reproduce, or a repository showing the error?
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
@@ -0,0 +1,24 @@
---
name: Setup issue
about: Trouble getting your own version of the theme running
title: "[setup]"
labels: setup
assignees: ''
---
## Link to your source code
https://github.com/...
_this helps understand even better the context, and in some occasions will make it possible to get a PR to get the issues fixed_
_if your code is not publicly available it will be less prioritized to give support - as it will not be as beneficial for the community_
## What do you want to achieve?
_describe what you are trying to do_
## What have you tried so far?
_describe what steps you have taken: have you created your site, are you customizing the theme, which version are you using..._
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[IDEA]"
labels: enhancement
assignees: zetxek
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+214
View File
@@ -0,0 +1,214 @@
# Adritian Hugo Theme - AI Coding Instructions
## Project Overview
This is a **Hugo theme** for personal websites/portfolios, not a standalone site (although a example site is provided, to demonstrate the usage of the theme itself). The theme provides layouts, styling, and functionality that users import via Hugo modules or as a git submodule.
**Technology Stack:**
- **Static Site Generator:** Hugo (v0.136+ extended version required)
- **CSS Framework:** Bootstrap 5 (v5.3.8)
- **CSS Preprocessor:** SCSS/Sass
- **JavaScript Libraries:** Fuse.js (search), DOMPurify (XSS protection)
- **Testing:** Playwright (E2E tests)
- **Package Manager:** npm
- **Templating:** Hugo Templates (Go templates)
- **Build Tools:** Hugo's built-in asset pipeline, PostCSS, Autoprefixer
## Project Architecture
This is a **Hugo theme** for personal websites/portfolios, not a standalone site. The theme provides layouts, styling, and functionality that users import via Hugo modules or as a git submodule.
The theme provides a example site (`exampleSite/`) that demonstrates all features and serves as a starting point for users. It is also used for the integration tests.
**Key Structure:**
- `layouts/` - Hugo templates (partials, shortcodes, content types)
- `assets/scss/` - Bootstrap 5-based styling with theme customizations
- `exampleSite/` - Demo content that gets distributed to users via `adritian-theme-helper` and serves as a github template repository
- `archetypes/` - Content templates for different content types
- `i18n/` - Multilingual translations (en/es/fr)
## Content Architecture & Custom Types
The theme implements **5 custom content types** with specific frontmatter:
1. **Experience** (`content/experience/`) - Job history with `jobTitle`, `company`, `location`, `duration`
2. **Skills** (`content/skills/`) - Technical skills with progress bars and categories
3. **Blog** (`content/blog/`) - Posts with sidebar/default layouts
4. **Client-work** (`content/client-work/`) - Portfolio pieces
5. **Testimonial** (`content/testimonial/`) - User testimonials
**Critical:** Each content type has a corresponding archetype in `archetypes/` and layout in `layouts/`. Always maintain this trio when adding new types.
## Development Workflows
### Local Development
```bash
# Install dependencies
npm install # Installs Bootstrap + theme-helper
# Theme development (testing with exampleSite)
npm run serve
```
### Testing
```bash
npm test # Full test suite
npm run test:e2e # Playwright E2E tests
npm run test:e2e:no-menus # Tests without navigation (for menu-less configs)
```
**Test Coverage:** E2E tests validate theme switching, language switching, content rendering, search functionality, and HTML validation across all major features.
### Performance Optimization
```bash
npm run build:performance # Optimized build with critical CSS
node scripts/optimize-performance.js # Performance analysis
```
## Styling System
**SCSS Architecture:** Modular approach extending Bootstrap 5
- `assets/scss/adritian.scss` - Main entry point importing all partials
- `assets/scss/_variables.scss` - Theme color/sizing variables
- Individual component files: `_navbar.scss`, `_blog.scss`, `_experience.scss`, etc.
**Theme Colors:** Uses CSS custom properties with `$base-color` as primary. Dark/light themes switch via CSS variables, not separate stylesheets.
## Multilingual (i18n) Implementation
**Translation Strategy:**
- Static strings: `i18n/*.yaml` files (navigation, UI elements)
- Content: Hugo's file-based translation (`content/en/`, `content/es/`, etc.)
- **Post-v1.7.0:** Prefers shortcode-based content over i18n strings for flexibility
## Shortcode System
**Primary Content Method:** Pages built with shortcodes rather than traditional Hugo sections.
Key shortcodes in `layouts/shortcodes/`:
- `experience-section` - Displays limited experience list (controlled by `homepageExperienceCount`)
- `experience-list` - Print-friendly experience list
- `contact-section` - Formspree integration
- `showcase-section` - Portfolio display
- `about-section`, `text-section` - Content blocks
**Pattern:** Most homepage sections use shortcodes for maximum customization flexibility.
## Module Distribution Model
**Critical Understanding:** This theme is distributed via:
1. **Hugo Module** (recommended) - `hugo mod get github.com/zetxek/adritian-free-hugo-theme`
2. **Theme Helper Package** - `@zetxek/adritian-theme-helper` downloads demo content
3. **Git Submodule** (legacy) - Direct theme inclusion
**Bootstrap Process:**
```bash
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme
hugo mod npm pack
npm install # Installs Bootstrap + theme-helper
./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
```
## Configuration Patterns
**Hugo Config (`hugo.toml`):**
- Performance optimizations: `[minify]`, `[imaging]`, `[markup]` sections
- Custom output formats for content types (footer content excluded from HTML generation)
- Module imports section for theme loading
**Package.json Scripts:**
- `serve` - Development with exampleSite
- `build` - Production build with minification
- Separate test commands for different scenarios
## Key Integration Points
**Search:** Powered by Fuse.js with JSON index generation
**Contact Forms:** Formspree integration via shortcode
**Analytics:** Vercel Analytics support built-in
**Performance:** Critical CSS generation, image optimization, minification
## Common Development Pitfalls
1. **Missing Dependencies:** Theme requires Bootstrap via npm - users often miss `npm install` step
2. **Content Structure:** Custom content types require specific frontmatter fields - validate against archetypes
3. **Translation Scope:** Post-v1.7.0 prefers shortcode content over i18n strings
4. **Module vs Submodule:** Most issues stem from incorrect installation method
## Testing Philosophy
E2E tests validate real user workflows rather than individual components. Tests include theme switching, language switching, navigation, search, and content rendering across different configurations (with/without menus).
When modifying layouts or adding features, ensure corresponding E2E tests exist in `tests/e2e/`.
## Coding Standards and Conventions
### HTML/Hugo Templates
- Use semantic HTML5 elements for proper document structure
- Follow Hugo's template naming conventions (lowercase with hyphens)
- Partial templates should be in `layouts/partials/`
- Shortcodes should be in `layouts/shortcodes/`
- Use Hugo's built-in functions when available (e.g., `absURL`, `relURL`)
### SCSS/CSS
- Follow Bootstrap 5 conventions and utility classes where possible
- Use BEM naming methodology for custom components when needed
- Prefix custom variables with theme-specific identifiers
- Keep files modular - one component per file
- Use CSS custom properties for themeable values
- File naming: lowercase with underscores (e.g., `_navbar.scss`, `_blog.scss`)
### JavaScript
- Use vanilla JavaScript (no jQuery)
- Prefer modern ES6+ syntax
- Keep JavaScript minimal - theme is primarily CSS-driven
- Use DOMPurify for any user-generated content sanitization
- Ensure all JavaScript is progressively enhanced (works without JS when possible)
### Content and Configuration
- YAML frontmatter for content files
- TOML for Hugo configuration files
- Follow existing frontmatter patterns for each content type
- Use descriptive, human-readable keys
### File Naming Conventions
- Templates: lowercase with hyphens (e.g., `single.html`, `list.html`)
- SCSS files: lowercase with underscores, prefixed with `_` for partials
- Content files: lowercase with hyphens (e.g., `my-blog-post.md`)
- Archetypes: match content type name (e.g., `blog.md`, `experience.md`)
## Web Accessibility Requirements
**This theme prioritizes accessibility (WCAG 2.1 AA compliance):**
- All interactive elements must be keyboard accessible
- Proper ARIA labels and roles for non-semantic elements
- Color contrast ratios must meet WCAG AA standards (4.5:1 for normal text, 3:1 for large text)
- Images must have descriptive `alt` attributes
- Forms must have associated labels
- Focus indicators must be visible and clear
- Dark/light theme switching must maintain accessibility standards in both modes
- Skip navigation links for keyboard users
- Semantic HTML structure (proper heading hierarchy, landmarks, etc.)
- Test changes with screen readers when modifying navigation or interactive elements
## Security Best Practices
- Never commit secrets, API keys, or sensitive data
- Use DOMPurify for sanitizing any user-generated or external content
- Validate and sanitize all user inputs in contact forms
- Follow Hugo's security best practices for template rendering
- Keep dependencies updated to patch security vulnerabilities
- Use Hugo's built-in `safeHTML`, `safeCSS`, `safeJS` functions appropriately
## Files and Directories to Ignore
Copilot should **ignore** or be cautious with:
- `node_modules/` - Third-party dependencies
- `public/` - Hugo build output (generated files)
- `resources/` - Hugo's asset cache
- `.hugo_build.lock` - Hugo build lock file
- `package-lock.json` - npm lock file (only update via npm commands)
- `.git/` - Version control directory
- `exampleSite/public/` - Example site build output
- `exampleSite/resources/` - Example site cache
- `.DS_Store` - macOS system files
+18
View File
@@ -0,0 +1,18 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
day: "friday"
- package-ecosystem: "github-actions"
directory: "/.github/"
schedule:
interval: "weekly"
day: "friday"
+7
View File
@@ -0,0 +1,7 @@
{
"ci": {
"collect": {
"staticDistDir": "./built-site"
}
}
}
+25
View File
@@ -0,0 +1,25 @@
name: Update contributors
permissions:
contents: write
id-token: write
pull-requests: read
on:
workflow_dispatch:
push:
branches:
- main
jobs:
contrib-readme-job:
runs-on: ubuntu-latest
name: Readme contributors
permissions:
contents: write
pull-requests: write
steps:
- name: Contribute List
uses: akhilmhdh/contributors-readme-action@v2.3.10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+46
View File
@@ -0,0 +1,46 @@
name: Crease release
permissions:
contents: write
id-token: write
pages: write
pull-requests: write
checks: write
statuses: write
deployments: write
on:
push:
tags:
- "v*.*.*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
- name: Set up Node.js
uses: actions/setup-node@v4
- name: Install Go dependencies
run: go mod download
- name: Install NPM dependencies
run: npm ci
- name: Get version
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Create ZIP archive
run: zip -r "adritian-free-hugo-theme-${{ steps.get_version.outputs.VERSION }}.zip" . -x "*.git*"
- name: Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: "adritian-free-hugo-theme-${{ steps.get_version.outputs.VERSION }}.zip"
+231
View File
@@ -0,0 +1,231 @@
# Workflow to verify that the exampleSite runs ok
name: Test example site
permissions:
contents: read
id-token: write
pages: write
pull-requests: write
checks: write
statuses: write
on:
# Runs on pull requests targeting the default branch
pull_request:
branches: ["main"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
env:
CI: true
jobs:
# Prepare dependencies job
prepare:
runs-on: ubuntu-latest
env:
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Cache Node Modules
id: cache-node-modules
uses: actions/cache@v4
with:
path: |
node_modules
key: modules-${{ hashFiles('package-lock.json') }}
- name: Cache Playwright Browsers
id: cache-playwright
uses: actions/cache@v4
with:
path: |
~/.cache/ms-playwright
key: playwright-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
if: steps.cache-playwright.outputs.cache-hit != 'true'
run: npx playwright install --with-deps
# Test with no menus
test-no-menus:
needs: prepare
runs-on: ubuntu-latest
env:
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Restore Node Modules
uses: actions/cache/restore@v4
with:
path: |
node_modules
key: modules-${{ hashFiles('package-lock.json') }}
- name: Restore Playwright Browsers
uses: actions/cache/restore@v4
with:
path: |
~/.cache/ms-playwright
key: playwright-${{ hashFiles('package-lock.json') }}
- name: Build site (no menus)
run: |
cd exampleSite
hugo server --themesDir ../.. --buildDrafts --buildFuture --bind 0.0.0.0 --config hugo.toml,hugo.disablemenu.toml &
sleep 10
- name: Run Playwright tests (no menus)
run: npm run test:e2e:no-menus
- name: Get the failure count
id: tests
run: |
export count=$(cat ./playwright-report/result.txt | grep Failed | cut -d ':' -f 2 | tr -d ' ')
echo "fail_count=$count" >> "$GITHUB_OUTPUT"
echo $count
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report-no-menus
path: playwright-report/
retention-days: 30
outputs:
fail_count: ${{ steps.tests.outputs.fail_count }}
# Test with menus
test-with-menus:
needs: prepare
runs-on: ubuntu-latest
env:
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Restore Node Modules
uses: actions/cache/restore@v4
with:
path: |
node_modules
key: modules-${{ hashFiles('package-lock.json') }}
- name: Restore Playwright Browsers
uses: actions/cache/restore@v4
with:
path: |
~/.cache/ms-playwright
key: playwright-${{ hashFiles('package-lock.json') }}
- name: Build and serve demo site (with menus)
run: |
cd exampleSite
hugo server --themesDir ../.. --buildDrafts --buildFuture --bind 0.0.0.0 &
sleep 10
- name: Run Playwright tests (with menus)
run: npm run test:e2e
- name: Get the failure count
id: tests
run: |
export count=$(cat ./playwright-report/result.txt | grep Failed | cut -d ':' -f 2 | tr -d ' ')
echo "fail_count=$count" >> "$GITHUB_OUTPUT"
echo $count
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report-with-menus
path: playwright-report/
retention-days: 30
outputs:
fail_count: ${{ steps.tests.outputs.fail_count }}
# Combine reports and publish
publish-reports:
if: github.ref == 'refs/heads/main'
continue-on-error: true
needs: [test-no-menus, test-with-menus]
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Download no-menus report
uses: actions/download-artifact@v4
with:
name: playwright-report-no-menus
path: playwright-report-no-menus
- name: Download with-menus report
uses: actions/download-artifact@v4
with:
name: playwright-report-with-menus
path: playwright-report-with-menus
- name: Combine reports
run: |
mkdir -p combined-report
cp -r playwright-report-no-menus/* combined-report/
mkdir -p combined-report/with-menus
cp -r playwright-report-with-menus/* combined-report/with-menus/
- name: Upload combined artifact for pages
if: always()
uses: actions/upload-pages-artifact@v3
with:
path: "./combined-report"
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
- name: Check for test failures
if: ${{ needs.test-no-menus.outputs.fail_count != '0' || needs.test-with-menus.outputs.fail_count != '0' }}
run: exit 1
+443
View File
@@ -0,0 +1,443 @@
name: Module Init, HTML and Lighthouse Check
permissions:
contents: read
id-token: write
pull-requests: write
checks: write
statuses: write
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test-module-init:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: ${{ vars.HUGO_VERSION || 'latest' }}
extended: true
- name: Create test site
run: |
hugo new site test-site
cd test-site
- name: Initialize Hugo module
working-directory: ./test-site
run: |
hugo mod init github.com/zetxek/test-site
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme
- name: Configure module ref
if: github.ref != 'refs/heads/main'
working-directory: ./test-site
run: |
# Use the branch that initiated the PR
branch_name=${GITHUB_HEAD_REF}
echo "Using branch: ${branch_name}"
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme@${branch_name}
- name: Configure Hugo module in hugo.toml
working-directory: ./test-site
run: |
echo '[module]
[[module.imports]]
path = "github.com/zetxek/adritian-free-hugo-theme"' > hugo.toml
- name: Validate Hugo module configuration
working-directory: ./test-site
run: |
hugo mod verify
hugo mod graph
- name: Upload test site
uses: actions/upload-artifact@v4
with:
name: test-site
path: test-site
retention-days: 1
build-site:
needs: test-module-init
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: 'latest'
extended: true
- name: Download test site
uses: actions/download-artifact@v4
with:
name: test-site
path: test-site
- name: Pack dependencies
working-directory: ./test-site
run: |
hugo mod npm pack
- name: (debug) Cat package.json
working-directory: ./test-site
run: |
cat package.json
- name: Npm install (bootstrap)
working-directory: ./test-site
run: |
npm install
- name: (debug) ls node_modules
working-directory: ./test-site
run: |
ls node_modules
- name: Run helper script (copy config and content)
working-directory: ./test-site
run: |
./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
- name: Run build
working-directory: ./test-site
run: hugo
- name: Upload built site
uses: actions/upload-artifact@v4
with:
name: built-site
path: test-site/public
retention-days: 1
validate-html:
needs: build-site
runs-on: ubuntu-latest
steps:
- name: Download built site
uses: actions/download-artifact@v4
with:
name: built-site
path: ./public
- name: Print index.html
working-directory: ./public
run: |
cat index.html
# Validate HTML syntax with https://github.com/marketplace/actions/html5-validator
- name: Validate HTML syntax
uses: Cyb3r-Jak3/html5validator-action@v7.2.0
with:
root: ./public
skip_git_check: true
lighthouse-check:
needs: build-site
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download built site
uses: actions/download-artifact@v4
with:
name: built-site
path: ./public
- name: Create output directory
run: mkdir -p ${{ github.workspace }}/tmp/artifacts
- name: Start local server
run: |
npx http-server ./public -p 8080 &
SERVER_PID=$!
echo "Started http-server with PID: $SERVER_PID"
# Wait for server to be ready (max 30 seconds)
for i in {1..30}; do
if curl -s http://localhost:8080 > /dev/null 2>&1; then
echo "✅ Server is ready and responding"
break
fi
echo "Waiting for server to start... ($i/30)"
sleep 1
done
# Verify server is actually responding
if ! curl -s http://localhost:8080 > /dev/null 2>&1; then
echo "❌ Server failed to start properly"
exit 1
fi
echo "Server is ready, proceeding with Lighthouse check"
- name: Lighthouse Check
id: lighthouse
uses: foo-software/lighthouse-check-action@v12.0.1
with:
device: 'all'
gitHubAccessToken: ${{ secrets.GITHUB_TOKEN }}
prCommentEnabled: false
urls: 'http://localhost:8080'
outputDirectory: ${{ github.workspace }}/tmp/artifacts
- name: Extract and save Lighthouse scores
id: extract-scores
run: |
# Extract scores from Lighthouse reports
mkdir -p .github
results_file="${{ github.workspace }}/tmp/artifacts/results.json"
if [ ! -f "$results_file" ]; then
echo "❌ Error: results.json not found"
echo '{"timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","commit":"${{ github.sha }}","scores":{}}' > current-scores.json
exit 0
fi
echo "✅ Found results.json, extracting scores..."
echo "{" > current-scores.json
echo ' "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",' >> current-scores.json
echo ' "commit": "${{ github.sha }}",' >> current-scores.json
echo ' "scores": {' >> current-scores.json
# The results.json from lighthouse-check-action contains an array of results
# Scores are already 0-100, not 0-1
num_results=$(jq 'length' "$results_file")
first_device=true
# Loop through results and organize by emulatedFormFactor
for i in $(seq 0 $((num_results - 1))); do
form_factor=$(jq -r ".[$i].emulatedFormFactor // \"unknown\"" "$results_file")
perf=$(jq -r ".[$i].scores.performance // 0" "$results_file")
a11y=$(jq -r ".[$i].scores.accessibility // 0" "$results_file")
bp=$(jq -r ".[$i].scores.bestPractices // 0" "$results_file")
seo=$(jq -r ".[$i].scores.seo // 0" "$results_file")
echo " 📱 $form_factor: Performance=$perf, Accessibility=$a11y, Best Practices=$bp, SEO=$seo"
if [ "$form_factor" = "mobile" ] || [ "$form_factor" = "desktop" ]; then
if [ "$first_device" = false ]; then
echo "," >> current-scores.json
fi
first_device=false
echo " \"$form_factor\": {" >> current-scores.json
echo " \"performance\": $perf," >> current-scores.json
echo " \"accessibility\": $a11y," >> current-scores.json
echo " \"best-practices\": $bp," >> current-scores.json
echo " \"seo\": $seo" >> current-scores.json
echo -n " }" >> current-scores.json
fi
done
echo "" >> current-scores.json
echo " }" >> current-scores.json
echo "}" >> current-scores.json
echo "✅ Scores extracted successfully"
- name: Load baseline scores from lighthouse-data branch
id: load-baseline
if: github.event_name == 'pull_request'
run: |
# Try to fetch baseline scores from lighthouse-data branch
if git show origin/lighthouse-data:.github/lighthouse-scores.json > baseline-scores.json 2>/dev/null; then
echo "baseline_exists=true" >> $GITHUB_OUTPUT
baseline_commit=$(jq -r '.commit // ""' baseline-scores.json)
echo "✅ Baseline scores loaded from lighthouse-data branch (commit: ${baseline_commit:0:7})"
else
echo "baseline_exists=false" >> $GITHUB_OUTPUT
echo "️ No baseline found - this will be the first baseline after merge"
# Create empty baseline
echo '{"commit":"","scores":{}}' > baseline-scores.json
fi
- name: Format Lighthouse results with comparison
id: format-results
run: |
# Function to get score emoji
get_emoji() {
score=$1
# Handle empty or invalid scores
if [ -z "$score" ] || [ "$score" = "0" ] || [ "$score" = "null" ]; then
echo "⚪"
return
fi
if (( $(echo "$score >= 90" | bc -l) )); then
echo "🟢"
elif (( $(echo "$score >= 50" | bc -l) )); then
echo "🟠"
else
echo "🔴"
fi
}
# Function to get delta indicator
get_delta() {
current=$1
baseline=$2
if [ "$baseline" = "null" ] || [ -z "$baseline" ] || [ "$baseline" = "0" ]; then
echo ""
return
fi
delta=$((current - baseline))
if [ $delta -gt 0 ]; then
echo "(↑ +$delta)"
elif [ $delta -lt 0 ]; then
echo "(↓ $delta)"
else
echo "(→)"
fi
}
# Build URLs
commit_sha="${{ github.sha }}"
commit_url="https://github.com/${{ github.repository }}/commit/$commit_sha"
artifacts_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
echo "## 🔦 Lighthouse Performance Report" > comment.md
echo "" >> comment.md
echo "**Test Results for commit:** [\`${commit_sha:0:7}\`]($commit_url)" >> comment.md
echo "" >> comment.md
# Check if we have baseline scores
has_baseline=false
if [ -f "baseline-scores.json" ]; then
baseline_commit=$(jq -r '.commit // ""' baseline-scores.json)
if [ -n "$baseline_commit" ] && [ "$baseline_commit" != "null" ]; then
has_baseline=true
baseline_commit_url="https://github.com/${{ github.repository }}/commit/$baseline_commit"
echo "_Comparing against baseline from lighthouse-data branch (commit: [\`${baseline_commit:0:7}\`]($baseline_commit_url))_" >> comment.md
echo "" >> comment.md
fi
fi
# Check if current scores exist
if [ ! -f "current-scores.json" ]; then
echo "❌ **Error:** No Lighthouse scores were generated." >> comment.md
echo "" >> comment.md
echo "Check the workflow logs for more details." >> comment.md
exit 0
fi
# Parse and format the results
found_scores=false
for device in "mobile" "desktop"; do
current_perf=$(jq -r ".scores.$device.performance // 0" current-scores.json)
current_a11y=$(jq -r ".scores.$device.accessibility // 0" current-scores.json)
current_bp=$(jq -r ".scores.$device[\"best-practices\"] // 0" current-scores.json)
current_seo=$(jq -r ".scores.$device.seo // 0" current-scores.json)
# Check if we have any real scores (not just 0)
if [ "$current_perf" != "0" ] || [ "$current_a11y" != "0" ] || [ "$current_bp" != "0" ] || [ "$current_seo" != "0" ]; then
found_scores=true
echo "### ${device^} Results" >> comment.md
echo "" >> comment.md
if [ "$has_baseline" = true ]; then
echo "| Category | Score | Change |" >> comment.md
echo "|----------|-------|--------|" >> comment.md
baseline_perf=$(jq -r ".scores.$device.performance // 0" baseline-scores.json)
baseline_a11y=$(jq -r ".scores.$device.accessibility // 0" baseline-scores.json)
baseline_bp=$(jq -r ".scores.$device[\"best-practices\"] // 0" baseline-scores.json)
baseline_seo=$(jq -r ".scores.$device.seo // 0" baseline-scores.json)
echo "| Performance | $(get_emoji $current_perf) $current_perf | $(get_delta $current_perf $baseline_perf) |" >> comment.md
echo "| Accessibility | $(get_emoji $current_a11y) $current_a11y | $(get_delta $current_a11y $baseline_a11y) |" >> comment.md
echo "| Best Practices | $(get_emoji $current_bp) $current_bp | $(get_delta $current_bp $baseline_bp) |" >> comment.md
echo "| SEO | $(get_emoji $current_seo) $current_seo | $(get_delta $current_seo $baseline_seo) |" >> comment.md
else
echo "| Category | Score |" >> comment.md
echo "|----------|-------|" >> comment.md
echo "| Performance | $(get_emoji $current_perf) $current_perf |" >> comment.md
echo "| Accessibility | $(get_emoji $current_a11y) $current_a11y |" >> comment.md
echo "| Best Practices | $(get_emoji $current_bp) $current_bp |" >> comment.md
echo "| SEO | $(get_emoji $current_seo) $current_seo |" >> comment.md
fi
echo "" >> comment.md
fi
done
if [ "$found_scores" = false ]; then
echo "⚠️ **Warning:** No Lighthouse scores were found in the reports." >> comment.md
echo "" >> comment.md
echo "This may be because:" >> comment.md
echo "- The Lighthouse action did not generate reports" >> comment.md
echo "- The report files have an unexpected format" >> comment.md
echo "" >> comment.md
fi
echo "📊 **[View full detailed reports in workflow artifacts]($artifacts_url)**" >> comment.md
echo "" >> comment.md
echo "_Report generated at $(date)_" >> comment.md
- name: Update PR comment
uses: marocchino/sticky-pull-request-comment@v2
if: github.event_name == 'pull_request'
with:
header: lighthouse-report
path: comment.md
- name: Upload Lighthouse report
uses: actions/upload-artifact@v4
with:
name: lighthouse-report
path: ${{ github.workspace }}/tmp/artifacts
- name: Save baseline scores to lighthouse-data branch
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
# Copy scores to temp location before branch switching
# (git clean will remove it from the working directory)
TEMP_SCORES="/tmp/lighthouse-scores-${{ github.sha }}.json"
cp "${{ github.workspace }}/current-scores.json" "$TEMP_SCORES"
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Try to fetch and checkout the lighthouse-data branch
if git fetch origin lighthouse-data 2>/dev/null && git checkout lighthouse-data 2>/dev/null; then
echo "✅ Checked out existing lighthouse-data branch"
else
echo "Creating new lighthouse-data orphan branch"
git checkout --orphan lighthouse-data
git rm -rf . 2>/dev/null || true
git clean -fd 2>/dev/null || true
fi
# Copy the lighthouse scores file from temp location
mkdir -p .github
cp "$TEMP_SCORES" .github/lighthouse-scores.json
# Commit and push if there are changes
git add .github/lighthouse-scores.json
if git diff --staged --quiet 2>/dev/null; then
echo "No changes to lighthouse scores"
else
git commit -m "chore: update lighthouse baseline scores [skip ci]"
git push origin lighthouse-data
echo "✅ Lighthouse baseline scores updated in lighthouse-data branch"
fi
# Clean up temp file
rm -f "$TEMP_SCORES"
+43
View File
@@ -0,0 +1,43 @@
## Updates demo site (https://github.com/zetxek/adritian-demo)
## triggers the update-hugo-module workflow in adritian-demo
## and the update-git-submodule workflow in the git-submodule-demo site
name: Update demo repos
permissions:
contents: read
id-token: write
pages: write
pull-requests: write
checks: write
statuses: write
actions: write
on:
push:
## run on push to main or a new semantic tag (ie: v1.2.3)
branches:
- main
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
workflow_dispatch:
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Trigger Update Hugo Module workflow
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.PRIVATE_TOKEN_GITHUB }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/zetxek/adritian-demo/actions/workflows/update-hugo-module.yml/dispatches \
-d '{"ref":"main"}'
- name: Trigger Update Git Submodule workflow
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.PRIVATE_TOKEN_GITHUB }}" \
-H "Accept: application/vnd.github.v3+json" \
https://github.com/zetxek/adritian-git-submodule-demo/actions/workflows/reset-submodule.yml/dispatches \
-d '{"ref":"main"}'
@@ -0,0 +1,147 @@
## Updates demo site (https://github.com/zetxek/adritian-demo)
## taken from https://stackoverflow.com/a/68213855/570087
name: PR demo site (on external PR)
permissions:
contents: read
id-token: write
pages: write
pull-requests: write
checks: write
statuses: write
actions: write
## This will open a PR which will open a vercel preview URL in the demo site
on:
pull_request_target:
types: [labeled]
workflow_dispatch:
jobs:
update-demo:
env:
SOURCE_BRANCH_NAME: ${{ github.head_ref || github.ref_name }} # PR branch name
PR_NUMBER: ${{ github.event.number }}
runs-on: ubuntu-latest
if: contains(github.event.pull_request.labels.*.name, 'safe to test')
steps:
- name: Checkout source repository (theme)
uses: actions/checkout@v4
with:
path: theme-source
ref: ${{ github.head_ref }}
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
- name: Checkout demo repository
uses: actions/checkout@v4
with:
repository: zetxek/adritian-demo
path: demo-repo
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
- name: Dump github context
run: echo "$GITHUB_CONTEXT"
shell: bash
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
- name: Send pull-request
run: |
LATEST_TAG=$(cd theme-source && git describe --tags --always)
LATEST_COMMIT=$(cd theme-source && git rev-parse HEAD)
SOURCE_REPOSITORY="zetxek/adritian-free-hugo-theme"
REPOSITORY="zetxek/adritian-demo"
REPO_NAME=${{ github.event.pull_request.head.repo.full_name }}
FOLDER="bin/$REPOSITORY"
PR_URL="https://github.com/$SOURCE_REPOSITORY/pull/$PR_NUMBER"
BRANCH_NAME="theme-update/update-theme-to-$LATEST_TAG"
BASE_BRANCH="main"
ASSIGNEE="zetxek"
echo "Latest tag: $LATEST_TAG"
echo "Latest commit: $LATEST_COMMIT"
echo "PR URL: $PR_URL"
git config --global --add --bool push.autoSetupRemote true
# Clone the remote repository and change working directory to the
# folder it was cloned to.
git clone \
--depth=1 \
--branch=main \
https://some-user:${{ secrets.PRIVATE_TOKEN_GITHUB }}@github.com/$REPOSITORY \
$FOLDER
cd $FOLDER
# Setup the committers identity.
git config user.email "actions@github.com"
git config user.name "GitHub Actions - update theme module version"
# Create a new feature branch for the changes.
echo "Working branch: $BRANCH_NAME"
git checkout -b $BRANCH_NAME
# Commit the changes and push the feature branch to origin
git config --global --add --bool push.autoSetupRemote true
echo "Committing all changes."
git add --all
COMMIT_MSG_THEME='chore: update theme module version to `'"$LATEST_TAG"'`'
echo 'Updating theme module: '"$COMMIT_MSG_THEME"
git commit -am "$COMMIT_MSG_THEME" && git push --force || echo "No changes to theme"
# Copy content from the checked-out theme source
cp ../../theme-source/exampleSite/hugo.toml hugo.toml
# Update URL
sed -i -e "s/\"https\:\/\/www\.adrianmoreno\.info\"/\"https\:\/\/adritian-demo\.vercel\.app\/\"/g" hugo.toml
COMMIT_MSG_CONFIG='chore: update config/content to `'"$LATEST_TAG"'` from https://github.com/zetxek/adritian-free-hugo-theme'
echo "Committing content/config: $COMMIT_MSG_CONFIG"
git commit -am "$COMMIT_MSG_CONFIG" && git push --force || echo "No changes to config"
echo "Pushing branch: $BRANCH_NAME"
git push origin $BRANCH_NAME --force
# Store the PAT in a file that can be accessed by the
# GitHub CLI.
echo "${{ secrets.PRIVATE_TOKEN_GITHUB }}" > token.txt
# Authorize GitHub CLI for the current repository and
# create a pull-requests containing the updates.
echo "Logging in to GitHub CLI."
gh auth login --with-token < token.txt
# Check if the PR already exists - if there's no "number" returned, we default to empty string
PR_EXISTS=$(gh pr list --state open --base $BASE_BRANCH --head $BRANCH_NAME --json number | jq '.[0].number // empty')
echo "PR_EXISTS: $PR_EXISTS"
# Check if there's a PR number. If the PR exists, update it (empty = it doesn't exist)
if [ -n "$PR_EXISTS" ]; then
echo "PR Exists. Updating pull-request..."
gh pr view
echo "✅ Pull-request created - done! "
# Else, we create it
else
echo "✨PR Does not exist yet. Creating pull-request..."
PR_TITLE='preview: update theme to `'$SOURCE_BRANCH_NAME'`'
echo 'PR title: '$PR_TITLE
PR_BODY="⚠️ The source PR is not merged yet - this is a preview PR.
🤖 This automated PR updates the theme version to a PR in the source repo: $PR_URL.
🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
echo "PR body: "$PR_BODY
gh pr create \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--head $BRANCH_NAME \
--base $BASE_BRANCH \
--assignee $ASSIGNEE \
--label preview
echo "✅ Pull-request created - done! "
fi
+260
View File
@@ -0,0 +1,260 @@
## Updates demo site (https://github.com/zetxek/adritian-demo)
## taken from https://stackoverflow.com/a/68213855/570087
name: Preview demo site
permissions:
contents: read
id-token: write
pages: write
pull-requests: write
checks: write
statuses: write
actions: write
## This will open a PR which will open a vercel preview URL in the demo site
on:
pull_request:
branches: [main]
workflow_dispatch:
jobs:
label-dependabot:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Add safe-to-test label to Dependabot PRs
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
try {
// First check if the label exists
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'safe-to-test'
});
// If it exists, add it to the PR
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['safe-to-test']
});
console.log('Added safe-to-test label to PR');
} catch (error) {
// If getting the label failed, the PR workflow can't add it
// Let's just modify our workflow to handle this differently
console.log('Could not add safe-to-test label: ' + error.message);
console.log('Will handle Dependabot PRs directly in check-conditions job');
}
check-dependabot:
runs-on: ubuntu-latest
outputs:
should-run: ${{ steps.check-conditions.outputs.should-run }}
steps:
- id: check-conditions
run: |
# Check if PR has safe-to-test label
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'safe-to-test') }}" == "true" ]]; then
echo "should-run=true" >> $GITHUB_OUTPUT
echo "PR has safe-to-test label, allowing workflow to run"
exit 0
fi
# Always allow Dependabot PRs to run
if [[ "${{ github.actor }}" == "dependabot[bot]" ]]; then
echo "should-run=true" >> $GITHUB_OUTPUT
echo "PR is from Dependabot, automatically allowing workflow to run"
exit 0
fi
# For other contributors
echo "should-run=true" >> $GITHUB_OUTPUT
echo "PR is from a trusted contributor, allowing workflow to run"
update-demo:
needs: check-dependabot
if: needs.check-dependabot.outputs.should-run == 'true'
env:
SOURCE_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
PR_NUMBER: ${{ github.event.number }}
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
runs-on: ubuntu-latest
steps:
- name: Checkout source repository
uses: actions/checkout@v4
with:
repository: zetxek/adritian-free-hugo-theme
ref: ${{ github.head_ref || github.ref_name }}
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
path: source-repo
- name: Checkout demo repository
uses: actions/checkout@v4
with:
repository: zetxek/adritian-demo
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
path: demo-repo
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true
- name: Send pull-request
shell: bash
run: |
SOURCE_REPOSITORY="zetxek/adritian-free-hugo-theme"
LATEST_COMMIT=${{ github.event.pull_request.head.sha || github.sha }}
SHORT_COMMIT=${LATEST_COMMIT:0:8}
# Handle both PR events and manual workflow dispatch
if [ -n "$PR_NUMBER" ]; then
PR_URL="https://github.com/$SOURCE_REPOSITORY/pull/$PR_NUMBER"
BRANCH_NAME="theme-update/pr-$PR_NUMBER-$SHORT_COMMIT"
PR_TITLE="preview: theme PR #$PR_NUMBER ($SHORT_COMMIT)"
else
# For manual workflow dispatch, use branch name instead
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
PR_URL="https://github.com/$SOURCE_REPOSITORY/tree/$BRANCH_REF"
BRANCH_NAME="theme-update/branch-$BRANCH_REF-$SHORT_COMMIT"
PR_TITLE="preview: theme branch $BRANCH_REF ($SHORT_COMMIT)"
fi
BASE_BRANCH="main"
ASSIGNEE="zetxek"
echo "Latest commit: $LATEST_COMMIT"
echo "Short commit: $SHORT_COMMIT"
echo "Branch name: $BRANCH_NAME"
echo "PR URL: $PR_URL"
if [ -n "$PR_NUMBER" ]; then
echo "PR Number: $PR_NUMBER"
else
echo "Manual workflow dispatch - no PR number"
fi
git config --global --add --bool push.autoSetupRemote true
# Verify source commit exists
cd source-repo
if ! git cat-file -e $LATEST_COMMIT; then
echo "Error: Commit $LATEST_COMMIT does not exist in source repository"
exit 1
fi
echo "✅ Verified commit $LATEST_COMMIT exists in source repository"
# Switch to demo repository
cd ../demo-repo
# Setup git identity
git config user.email "actions@github.com"
git config user.name "GitHub Actions - update theme module"
# Create feature branch
git checkout -b $BRANCH_NAME
# Initialize Hugo module if needed
if [ ! -f "go.mod" ]; then
hugo mod init github.com/zetxek/adritian-demo
fi
# Clean up any existing invalid module references
if [ -f "go.mod" ]; then
echo "Cleaning up existing go.mod..."
# Remove any existing theme references that might have invalid revisions
grep -v 'github.com/zetxek/adritian-free-hugo-theme' go.mod > go.mod.tmp || touch go.mod.tmp
mv go.mod.tmp go.mod
# Clean up any existing go.sum entries
if [ -f "go.sum" ]; then
grep -v 'github.com/zetxek/adritian-free-hugo-theme' go.sum > go.sum.tmp || touch go.sum.tmp
mv go.sum.tmp go.sum
fi
fi
# Get the theme from the PR branch - use branch name instead of commit for better reliability
BRANCH_NAME_THEME="${{ github.head_ref || github.ref_name }}"
echo "Using theme branch: $BRANCH_NAME_THEME"
# Try to get the specific commit first, fallback to branch if commit is not available
echo "Attempting to get theme with commit: $LATEST_COMMIT"
if hugo mod get github.com/zetxek/adritian-free-hugo-theme@$LATEST_COMMIT; then
echo "✅ Successfully got theme with commit hash"
else
echo "Failed to get specific commit, trying branch instead..."
echo "Attempting to get theme with branch: $BRANCH_NAME_THEME"
if hugo mod get github.com/zetxek/adritian-free-hugo-theme@$BRANCH_NAME_THEME; then
echo "✅ Successfully got theme with branch name"
else
echo "❌ Failed to get theme with both commit and branch, trying main branch as last resort..."
hugo mod get github.com/zetxek/adritian-free-hugo-theme@main
fi
fi
hugo mod tidy
# Commit changes
git add go.mod go.sum
if [ -n "$PR_NUMBER" ]; then
COMMIT_MSG="preview: theme PR #$PR_NUMBER ($SHORT_COMMIT)"
else
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
COMMIT_MSG="preview: theme branch $BRANCH_REF ($SHORT_COMMIT)"
fi
git commit -m "$COMMIT_MSG" && git push --force || echo "No changes to commit"
# Push branch
git push origin $BRANCH_NAME --force
# Setup GitHub CLI
echo "${{ secrets.PRIVATE_TOKEN_GITHUB }}" > token.txt
gh auth login --with-token < token.txt
# Check if PR exists
PR_EXISTS=$(gh pr list --state open --base $BASE_BRANCH --head $BRANCH_NAME --json number | jq '.[0].number // empty')
echo "PR_EXISTS: $PR_EXISTS"
if [ -n "$PR_EXISTS" ]; then
echo "PR Exists. Updating pull-request..."
gh pr view
echo "✅ Pull-request updated!"
# TODO: update the PR body with the latest commit
# Add a comment to the PR with the update
gh pr comment $PR_EXISTS --body "Updated PR to last commit in source repo: \`$LATEST_COMMIT\`"
else
echo "✨ Creating new pull-request..."
if [ -n "$PR_NUMBER" ]; then
PR_BODY="⚠️ The source PR is not merged yet - this is a preview PR created to test the theme update with a vercel preview URL.
- Last commit: \`$LATEST_COMMIT\`
- Source PR: $PR_URL
- 🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
else
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
PR_BODY="⚠️ This is a preview PR created to test the theme update from branch \`$BRANCH_REF\` with a vercel preview URL.
- Last commit: \`$LATEST_COMMIT\`
- Source branch: $PR_URL
- 🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
fi
PR_RESULT=$(gh pr create \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--head $BRANCH_NAME \
--base $BASE_BRANCH \
--assignee $ASSIGNEE \
--label preview)
if [ $? -ne 0 ]; then
echo "Error creating PR: $PR_RESULT"
exit 0
fi
echo "✅ Pull-request created!"
fi
@@ -0,0 +1,70 @@
name: Update Example Site
permissions:
contents: read
id-token: write
pages: write
pull-requests: write
checks: write
statuses: write
actions: write
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # Every Sunday at midnight
jobs:
update-example-site:
runs-on: ubuntu-latest
env:
DATE: ${{ format('YYYY-MM-DD', github.event.repository.pushed_at) }}
steps:
- name: Set date
run: echo "DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
- name: Set branch name
run: echo "BRANCH_NAME=update/exampleSite-${DATE}" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Node.js dependencies
run: npm i
- name: Update example site content
run: |
cd exampleSite
../node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
- name: Create new branch
run: |
git checkout -b ${{ env.BRANCH_NAME }}
- name: Commit changes
run: |
git config --global user.name 'GitHub Actions'
git config --global user.email 'actions@github.com'
git add .
git commit -m "Update example site content"
- name: Push changes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git push origin ${{ env.BRANCH_NAME }}
- name: Create Pull Request
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_TITLE="ExampleSite content update (${DATE})"
PR_BODY="This automated PR updates the example site with the latest content from [adritian-demo](https://github.com/zetxek/adritian-demo).
**Overview of changes:**
<code>
$(git diff --stat HEAD~1)
</code>"
gh pr create --title "$PR_TITLE" --body "$PR_BODY" --base main --head "${{ env.BRANCH_NAME }}"
+25
View File
@@ -0,0 +1,25 @@
Thumbs.db
.DS_Store
.dist
.tmp
.sass-cache
npm-debug.log
node_modules
builds
# Playwright
/test-results/
/tests/coverage-report
playwright-report
test-results
exampleSite/public
exampleSite/resources
exampleSite/.hugo_build.lock
exampleSite/hugo_stats.json
# Local builds
.hugo_build.lock
public/
# idea
.idea/
+19
View File
@@ -0,0 +1,19 @@
{
"plugins": [
"prettier-plugin-go-template"
],
"overrides": [
{
"files": [
"*.html"
],
"options": {
"parser": "go-template"
}
}
],
"htmlWhitespaceSensitivity": "ignore",
"bracketSameLine": true,
"goTemplateBracketSpacing": true
}
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"esbenp.prettier-vscode"
]
}
+128
View File
@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
Adrián Moreno Peña, zetxek@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+54
View File
@@ -0,0 +1,54 @@
# Contribution Guidelines
Thank you for considering contributing to our project! Contributions are very welcome 🙏
We appreciate your time and effort in helping us improve the project. To ensure a smooth collaboration, please follow these guidelines:
## Getting Started
1. **Fork the Repository**: Start by forking the repository to your GitHub account.
2. **Clone the Repository**: Clone the forked repository to your local machine using:
```bash
git clone https://github.com/zetxek/adritian-free-hugo-theme.git
```
3. **Create a Branch**: Create a new branch for your feature or bug fix:
```bash
git checkout -b feature/your-feature-name
```
## Making Changes
1. **Code Style**: Follow the project's coding style and conventions. Ensure your code is clean and well-documented.
2. **Commit Messages**: Write clear and concise commit messages. Use the present tense and include a brief description of the changes.
3. **Testing**: Ensure your changes work well with the test site (and others, maybe with yours), in mobile and desktop browsers.
## Submitting Changes
1. **Push Changes**: Push your changes to your forked repository:
```bash
git push origin feature/your-feature-name
```
2. **Create a Pull Request**: Open a pull request (PR) from your branch to this repository's `main` branch. Provide a detailed description of your changes and link any relevant issues.
## Code Review
1. **Review Process**: Your PR will be reviewed by project maintainers (mainly @zetxek). Be responsive to feedback and make necessary changes.
2. **Approval**: Once your PR is approved, it will be merged into the main branch.
🎗️ Remember that the maintainers are working in a voluntary basis - do not expect a quick turnaround, as they will get to the issue when life allows.
## Community Guidelines
You can read about our guidelines in our [code of conduct](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/CODE_OF_CONDUCT.md).
## Reporting Issues
1. **Bug Reports**: If you find a bug, please open an [issue](https://github.com/zetxek/adritian-free-hugo-theme/issues) with a clear description and steps to reproduce the problem.
2. **Feature Requests**: If you have an idea for a new feature, open an [issue](https://github.com/zetxek/adritian-free-hugo-theme/issues) to discuss it with the community.
Support is not offered in private, direct mails - as they won't directly benefit the community.
## License
By contributing to this project, you agree that your contributions will be licensed under the project's [LICENSE](LICENSE) file.
+130
View File
@@ -0,0 +1,130 @@
# New Features Documentation
This document describes the new features added to the Adritian Hugo theme to make it more competitive with other popular Hugo themes.
## Features Overview
### 1. Related Posts
**What it does:** Automatically displays related blog posts at the end of each post based on shared tags and publication dates.
**How to use:** No configuration needed - it works automatically for all blog posts.
**Customization:** The related content algorithm can be adjusted in `hugo.toml` under the `[related]` section.
### 2. Social Sharing Buttons
**What it does:** Adds sharing buttons for Twitter/X, LinkedIn, Facebook, and Email to all blog posts.
**How to use:** Enabled by default. To customize:
```toml
[params.sharing]
enabled = true # Set to false to disable
twitter = true # Show/hide individual platforms
linkedin = true
facebook = true
email = true
```
### 3. Table of Contents (TOC)
**What it does:** Auto-generates a table of contents from post headings for easy navigation.
**How to use:** Add to post frontmatter:
```yaml
---
toc: true
tocSticky: true # Optional: makes TOC sticky on desktop
---
```
**Requirements:** Only appears if post has more than 400 words.
### 4. Comments Integration
**What it does:** Supports three popular comment systems: Disqus, Giscus, and Utterances.
**How to use:**
#### Option 1: Disqus
```toml
disqusShortname = "your-shortname"
[params.comments]
enabled = true
provider = "disqus"
```
#### Option 2: Giscus (GitHub Discussions)
```toml
[params.comments]
enabled = true
provider = "giscus"
[params.comments.giscus]
repo = "username/repo"
repoId = "R_xxxxx"
category = "General"
categoryId = "DIC_xxxxx"
```
#### Option 3: Utterances (GitHub Issues)
```toml
[params.comments]
enabled = true
provider = "utterances"
[params.comments.utterances]
repo = "username/repo"
issueTerm = "pathname"
theme = "preferred-color-scheme"
```
### 5. Enhanced Reading Metadata
**What it does:** Displays additional metadata for blog posts:
- Estimated reading time
- Last modified date (if specified)
- Word count
**How to use:** Automatic for reading time and word count. For last modified:
```yaml
---
title: "My Post"
date: 2025-01-15
lastmod: 2025-02-20 # Add this field
---
```
## Styling
All new features include:
- Full dark mode support
- Responsive design for mobile/tablet/desktop
- Consistent styling with the theme
- Accessibility features (ARIA labels, semantic HTML)
## Translations
All features support the theme's multilingual capabilities with translations for:
- English (en)
- Spanish (es)
- French (fr)
- German (de)
- Dutch (nl)
- Danish (da)
- Italian (it)
- Portuguese (pt)
- Swedish (sv)
- Norwegian (no)
- Polish (pl)
Additional languages can be added by creating translation files in `i18n/`.
## Demo
See all features in action at the demo blog post:
`/blog/new-features-demo/` in the example site.
+20
View File
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright for portions of the theme are held by (c) 2020 Radity (https://radity.com). Other copyright is held by Adrián Moreno Peña, 2022.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+385
View File
@@ -0,0 +1,385 @@
# Adritian Free Hugo Theme
A modern, fast and extensible Hugo theme for personal websites and professional landing pages - with blog and portfolio support
[![Vercel Deploy](https://deploy-badge.vercel.app/vercel/adritian-demo?name=demo)
](https://adritian-demo.vercel.app/) [![Test example site](https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/test-example-site.yml/badge.svg)](https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/test-example-site.yml)
## 🚀 Key Features
- 🏎️ Fast, minimalistic code (no jQuery or other javascript frameworks)
- 🖼️ Bootstrap v5 (`v5.3.7`) CSS framework with Scss customization
- 📚 Multi-language (i18n) support
- 🛠️ Custom content types (work experience, education, projects/work showcase, testimonials, blog)
- 🧰 Multiple shortcodes to customize your landing page in any way you want
- 🔎 Lightning fast search (powered by [fuse.js](https://www.fusejs.io/))
- 💯 Perfect Lighthouse scores (Performance, Accessibility, SEO)
- 🌚 Automatic dark/light theme switching, with manual override
- 🖨️ Print-friendly CV template
- 🔧 Technical Skills showcase with visual skill bars and categories
- ⚡ Vercel-ready with Analytics & Speed Insights support
- 🖼️ Menus with icon support
- 🔗 **Related posts** - Automatically display related content based on tags
- 📤 **Social sharing buttons** - Built-in sharing for Twitter/X, LinkedIn, Facebook, and Email
- 📑 **Table of Contents** - Auto-generated, sticky TOC for blog posts
- 💬 **Comments integration** - Support for Disqus, Giscus, and Utterances
- ⏱️ **Reading time display** - Estimated reading time for blog posts
- 🏷️ **Enhanced taxonomy support** - Improved tags and categories display
The theme focuses on accessibility, high performance, and usability (it's very easy to get started). It's extendable by adding your own styles or content types, and it has a solid foundation on which to build.
Some of the best applications for the theme are for minimalistic websites, single-page applications, and personal portfolios. It has a contact form you can customize to your mail address without setting up a backend.
> [!TIP]
> Do you like the theme? 🌟 Star the repo on GitHub to help make it more visible
## Live demo & Preview
You can see it live at [www.adrianmoreno.info](https://www.adrianmoreno.info) (my personal website), as well as in these screenshots of the homepage, in the dark and light variations of the theme:
<table>
<tbody>
<tr>
<td>
<img src="https://user-images.githubusercontent.com/240085/230632835-74349170-d610-4731-8fac-62c413e6b3f5.png" alt="Light version of the Hugo theme Adritian"/>
</td>
<td>
<img src="https://raw.githubusercontent.com/zetxek/adritian-free-hugo-theme/main/images/screenshot-dark-fullscroll.jpeg" alt="Dark version of the Hugo theme Adritian"/>
</td>
</tr>
</tbody>
</table>
The dark color variation is selected automatically based on browser settings, and a color switcher is available in the footer and the mobile menu for visitors to override.
Other relevant repositories related to this theme are:
1. [adritian-theme-helper](https://github.com/zetxek/adritian-theme-helper): npm package helper, used to bootstrap the theme. It helps initialize a site with the right content and configuration files.
1. A full-featured site, [my personal website](https://www.adrianmoreno.info) [in github too](https://github.com/zetxek/adrianmoreno.info), so you can see how the theme can look real-life (including deployment scripts).
1. A simpler [demo site for the theme, adritian-demo](https://adritian-demo.vercel.app/) ([and its code](https://github.com/zetxek/adritian-demo)), where the demo content comes from.
1. The same demo site, in a [git submodules integration](https://github.com/zetxek/adritian-git-submodule-demo).
💡 For more inspiration, check this document's [showcase section](#showcase).
✨ This theme is entirely free and open source. We welcome your ideas, feedback, and contributions! If you find it useful, please give it a GitHub star to show your support.
## Quickstart
#### Install Hugo
This is a theme for the website generator Hugo. To use it, you must install Hugo by following [the official guide](https://gohugo.io/getting-started/installing/).
**We recommend installing the theme as a [Hugo module](https://gohugo.io/hugo-modules/) (recommended, and explained below).** This is the most powerful way to install the theme, will allow you to combine it with other modules, and is probably the easiest way to update across theme versions.
Another alternative is to use [git submodules](https://gohugo.io/getting-started/quick-start/#create-a-site) or to [download the theme as a zip file](https://github.com/zetxek/adritian-free-hugo-theme/releases) and copy the files to your site`*`. But that will make your site "stuck in time" and more difficult to upgrade. **This is not recommended or supported directly**.
> **On the release files:** `*` from the version `v1.5.4` the theme available as a zip file in the [releases page](https://github.com/zetxek/adritian-free-hugo-theme/releases) contains the `node_modules` folder, so you don't need to install it separately. This is a convenience for edge cases that might have problems installing the theme as a module or downloading many files.__
### As a Hugo Module (recommended)
> **Note:** Before proceeding, **Ensure you have Go and Hugo installed** and that you have created a new Hugo project.
As a pre-requirement, you will need Hugo set up and running. You can follow [the official guide for it](https://gohugo.io/categories/installation/).
The theme has been tested with Hugo version `0.136` (extended version). If you get errors regarding missing functionalities, check if you have the latest version of Hugo available.
**Note:** as mentioned, the theme supports both Hugo modules and git submodules. You should use Hugo modules to install the theme in the most maintainable way. If you prefer git submodules you can follow these [older instructions](https://gohugobrasil.netlify.app/themes/installing-and-using-themes/) or the next ones as help:
<details>
<summary>Step-by-step instructions to setup the theme as a hugo module</summary>
1. Create a new Hugo site (this will create a new folder): `hugo new site <your website's name>`
1. Enter the newly created folder: `cd <your website's name>/`
1. Initialize the Hugo Module system in your site if you haven't already: `hugo mod init github.com/username/your-site` (_you don't need to host your website on GitHub, you can add anything as a name_)
1. Install the theme as a module: `hugo mod get -u github.com/zetxek/adritian-free-hugo-theme`
1. Add the following to your `hugo.toml`, to set the theme as active:
```
[module]
[[module.imports]]
path = "github.com/zetxek/adritian-free-hugo-theme"
```
6. Prepare the `package.json` file: `hugo mod npm pack`
7. Install the dependencies: `npm install`. This will include Bootstrap (needed for styling) and the helper script [adritian-theme-helper](https://github.com/zetxek/adritian-theme-helper).
8. Run the initial content downloader: `./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js`. This will download the demo content from the [adritian-demo](https://github.com/zetxek/adritian-demo) repository and copy it to your site, for a quick start (including translations, images, configuration and content)
</details>
### Running the site
The initial configuration allows you to have a base to edit and adapt to your site, and see the available functionalities.
**Make sure to edit `baseURL`, `title` and `description`**. You can edit the header links, as well as the languages to your needs.
After you have installed the `npm` packages and setup the initial contents, you can
1. Start Hugo with `hugo server`...
1. 🎉 The theme is alive on http://localhost:1313/
For next steps and guidance on where to customize your content, [check the demo site](https://adritian-demo.vercel.app/).
You can check the demo site help page for other installation methods (such as submodules or manual configuration).
### Additional features and configuration
The theme is extensible and customizable in multiple areas, and it can be tricky to figure out exactly what to edit. This is a guide (that is complemented by the [demo site](https://adritian-demo.vercel.app/)).
<img width="1395" alt="image" src="https://github.com/user-attachments/assets/270c4445-5354-441a-ab23-21d91762e33c" />
#### Multi-language support
https://github.com/user-attachments/assets/030e765a-275f-4141-88e0-b854ebe551da
The theme implements the [internationalization (i18n) system by Hugo](https://gohugo.io/content-management/multilingual/), to enable multilingual sites.
See the content in the `i18n` folder to edit the translations, and the configuration `hugo.toml` to define your active languages. The theme includes translations for:
- **English** (`en`)
- **Spanish** (`es`)
- **French** (`fr`)
- **German** (`de`)
- **Dutch** (`nl`)
- **Danish** (`da`)
- **Italian** (`it`)
- **Portuguese** (`pt`)
- **Swedish** (`sv`)
- **Norwegian** (`no`)
- **Polish** (`pl`)
The example site has 3 enabled languages by default (`en`, `es`, and `fr`). You can enable additional languages by adding them to your `hugo.toml` configuration, or disable the provided ones (by setting `disabled` to `true` on the languages you don't need).
Most of the content is expected to be translated via the content system of Hugo:
- [by file name](https://gohugo.io/content-management/multilingual/#translation-by-file-name)
- [by content directory](https://gohugo.io/content-management/multilingual/#translation-by-content-directory)
Note: The introduction of i18n support was done in the version `v1.3.0` and it has breaking changes due to the way in which the content was managed. You can read about the upgrade path in [UPGRADING.md](UPGRADING.md). In the version `v1.7.0` the usage of Hugo's content management was expanded, to use less `i18n` strings and more file name/content directory based translations.
#### Editing the theme content
You can check the repository [adritian-demo](https://github.com/zetxek/adritian-demo) for a reference implementation, as well as the [theme website](https://adritian-demo.vercel.app/) (https://adritian-demo.vercel.app/), to get a visual guide on how to edit the content.
Following the initial setup instructions you will get a "ready-to-use" version of the site, with sample content for you to edit and customize.
#### Technical Skills Showcase
The theme includes a dedicated Technical Skills showcase section that allows you to visually display your skills with progress bars and categorization:
- Create a `/skills` section in your site with customizable skill categories
- Each skill can include name, proficiency level, years of experience, and description
- Visual skill bars automatically use your theme's primary color
- Full dark mode support with appropriate contrast
- Responsive design for all device sizes
- Accessible through navigation menu with multilingual support
To use this feature, create a `skills/_index.md` file with structured front matter for your skill categories and individual skills. The theme will automatically generate a visually appealing skills showcase page.
#### Shortcodes
The theme has multiple shortcodes available for use in the content, so you can customize your homepage (or any other page) as you want. You can read about them in the [shortcodes page](https://adritian-demo.vercel.app/blog/shortcodes). Since version `v1.7.0,` this is the preferred way to set up your theme content and translations, as that's the most flexible system.
You can see the shortcodes in use in the demo site's pages, like
- [home page](https://adritian-demo.vercel.app/) [`(source)`](https://raw.githubusercontent.com/zetxek/adritian-demo/refs/heads/main/content/home/home.md)
- [CV page](https://adritian-demo.vercel.app/cv) [`(source)`](https://raw.githubusercontent.com/zetxek/adritian-demo/refs/heads/main/content/cv.md)
#### Contact form
_(optional, if you want to use the contact form)_ edit the key `contact` in your `homepage.yml` file, to customize your mail address. Sign up in [formspree](https://formspree.io) to redirect mails to your own.
It can be rendered in any page via the `contact-section` shortcode.
#### Blog
Two layouts are available for the blog:
- `default` (full-width for posts)
- `sidebar` (sidebar with recent posts and categories)
| Default Layout | Sidebar Layout |
|---------------|----------------|
| ![Default blog layout with full width content](https://github.com/zetxek/adritian-free-hugo-theme/raw/main/images/blog-default.jpeg) | ![Blog layout with sidebar showing recent posts](https://github.com/zetxek/adritian-free-hugo-theme/raw/main/images/blog-sidebar.jpeg) |
| Full width posts | Posts with left sidebar |
| Clean, focused reading experience | Shows recent posts and categories |
| Maximizes content area | 25% width sidebar by default |
| Best for image-heavy posts | Helps with site navigation |
<img width="1271" alt="image" src="https://github.com/user-attachments/assets/1821a3b7-f572-4958-8c4f-bd1687cc8f71">
To use the blog, you can use the content type "blog", and render it in the URL `/blog`.
You can add a menu link to it in `hugo.toml`.
The posts will be markdown files stored in the `content/blog` folder.
The layout can be configured in the `hugo.toml` file, under the `[params.blog]` section:
```toml
[params.blog]
layout = "sidebar-right" # options: default (no sidebar), sidebar, sidebar-right
sidebarWidth = "25" # percentage width of the sidebar (when using sidebar layouts)
narrowContent = true # if true, limits content width to 720px for better readability (default: true)
showCategories = true # show categories in the sidebar
showRecentPosts = true # show recent posts in the sidebar
recentPostCount = 5 # number of recent posts to display
listStyle = "summary" # options: simple, summary
```
**Configuration options:**
- `layout`: Choose between `default` (no sidebar), `sidebar` (left sidebar), or `sidebar-right` (right sidebar)
- `sidebarWidth`: Set the percentage width of the sidebar (e.g., "25" for 25%)
- `narrowContent`: When set to `true` (default), limits the blog content width to 720px for optimal readability. Set to `false` for full-width content
- `showCategories`: Display category links in the sidebar
- `showRecentPosts`: Display recent posts in the sidebar
- `recentPostCount`: Number of recent posts to show in the sidebar
- `listStyle`: Choose between `simple` (title and date only) or `summary` (includes excerpt)
##### Blog Post Features
**Related Posts**
The theme automatically shows related posts at the end of each blog post based on shared tags and publish dates. This is powered by Hugo's built-in related content feature.
**Table of Contents**
Enable a table of contents for any blog post by adding these parameters to your post's frontmatter:
```yaml
---
toc: true
tocSticky: true # optional, makes TOC sticky on desktop
---
```
The TOC automatically generates from your post headings and only appears when the post has more than 400 words.
**Social Sharing**
Social sharing buttons are enabled by default for all blog posts. You can customize which platforms appear in your `hugo.toml`:
```toml
[params.sharing]
enabled = true
twitter = true
linkedin = true
facebook = true
email = true
```
**Comments**
The theme supports popular comment systems. Configure in your `hugo.toml`:
```toml
[params.comments]
enabled = true
provider = "giscus" # options: "disqus", "giscus", "utterances"
# For Giscus (GitHub Discussions)
[params.comments.giscus]
repo = "username/repo"
repoId = "your-repo-id"
category = "General"
categoryId = "your-category-id"
# For Utterances (GitHub Issues)
[params.comments.utterances]
repo = "username/repo"
issueTerm = "pathname"
theme = "preferred-color-scheme"
```
For Disqus, set your `disqusShortname` in the site configuration.
**Reading Time & Metadata**
Blog posts automatically display:
- Estimated reading time
- Word count
- Publication date
- Last modified date (if `lastmod` is set in frontmatter)
#### (Job) Experience
This functionality and content is especially suited for personal professional sites, showcasing the work experience:
<img width="1444" alt="SCR-20240624-uaoi" src="https://github.com/zetxek/adritian-free-hugo-theme/assets/240085/9ea86d6a-62c6-4c4f-96ba-8450fa24dd68">
It can be used to render job experience, projects and/or clients. Each experience/project has a duration, job title, company name, location and description/excerpt as well as a longer text.
The experience is managed through a specific content type (see `content/experience` for an example).
You can use `hugo new experience/experience-name.md` (replacing `experience-name` by the name of the job experience).
This will create the content in the `content/experience` folder, following the `experience` archetype.
The following fields are used from the file's Front Matter: `title`, `jobTitle`, `company`, `location`, `duration`.
You can find a sample experience file content here:
```
---
date: 2007-12-01T00:00:00+01:00
draft: false
title: "Job #1"
jobTitle: "Junior Intern"
company: "Internet Affairs Inc. "
location: "Stavanger, Norway"
duration: "2022-2024"
---
### Fixing the world, one byte at a time
The beginning of a great career.
```
The experience can be displayed in several locations and different styles:
| Shortcode usage | Experience page | Single experience item | Print-friendly list |
|---------------|----------------|----------------|----------------|
| ![Experience list with shortcode](https://github.com/user-attachments/assets/7e974632-824c-494c-9dbc-67bb96992517) | ![Experience page](https://github.com/user-attachments/assets/ad53f815-2bd4-4723-a9a1-28be2c01461a) | ![Single experience page](https://github.com/user-attachments/assets/97a20b98-df5c-4e07-9b2e-d7aba75ec81b) | ![CV Experience](https://github.com/user-attachments/assets/7d2d7e4b-a23c-4be7-9617-da7232ea11a7) |
| Experience list rendered via shortcode | Experience list in /experience | Experience item in in single page | Full-width print-friendly experience
| [🔗](https://adritian-demo.vercel.app/#experience-single) By using the shortcode `experience-section`: in a page (such as your homepage), with a limited number of experiences (controlled by the config parameter `homepageExperienceCount` in the file `hugo.toml`). The summary is displayed, as well as an introduction text and optional links | [🔗](https://adritian-demo.vercel.app/experience) Automatically, in the **Experience page**, in `/experience`, with a list of all experiences (no limit). The summary is displayed for each item, as well as a introduction text and optional links. | [🔗](https://adritian-demo.vercel.app/experience/job-1/) Individual experience page (such as `/experience/job-1`), where all details are displayed, and links to the other descriptions are available to navigate. | [🔗](https://adritian-demo.vercel.app/cv) Using the shortcode `experience-list` you can generate a list of experience that is not interactive - good for print-friendly layouts.
## Troubleshooting
This theme is a version of the one found on my website [adrianmoreno.info](https://www.adrianmoreno.info). If you run into trouble, [you can check the code on my website](https://github.com/zetxek/adrianmoreno.info) for reference.
If you have improvements for the theme, you are very welcome to make a PR if you are able 💕. Otherwise - see below for how to get help (and maybe help others with the same problem).
## Testing
The theme includes comprehensive E2E tests using Playwright to ensure all features work correctly.
### Running Tests
```bash
# Run all tests
npm test
# Run E2E tests only
npm run test:e2e
# Run new features tests specifically
npm run test:e2e:new-features
# Run tests in UI mode (interactive)
npm run test:e2e:ui
```
### Test Coverage
The test suite includes **27 comprehensive tests** for the new blog features:
- Related Posts functionality
- Social Sharing buttons (Twitter, LinkedIn, Facebook, Email)
- Table of Contents generation and navigation
- Enhanced reading metadata (reading time, word count, dates)
- Dark mode compatibility
- Responsive design
- Accessibility features
- Multilingual support
- Performance validation
See `tests/README.md` for detailed testing documentation.
### Common issues
- The site fails to build. Look for the last line of the stacktrace - if you find mentions to missing files, such as in
```
Error: error building site: TOCSS: failed to transform "/scss/adritian.scss" (text/x-scss): ".../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/zetxek/adritian-free-hugo-theme@v1.5.6/assets/scss/adritian.scss:1:1": File to import not found or unreadable: bootstrap/bootstrap.
```
Make sure that you have the dependencies installed. Check the troubleshooting steps in the [following issue](https://github.com/zetxek/adritian-free-hugo-theme/issues/194#issuecomment-2634193132).
- The site renders in a weird-looking way, or you miss content. Check that the co
+465
View File
@@ -0,0 +1,465 @@
# Upgrading across versions
This documentation is meant to help you upgrade across versions, when potentially breaking changes are introduced.
## v1.7.19
Adding a new page transition effect.
The new effect is applied to all pages, and is triggered when navigating through the site. It can be disabled in the `config.toml` file, under the `params.pageTransition` section.
By default, the effect is enabled.
## v1.7.12
__Introducing 🔎Search__
To be able to use the search functionality, you need to:
1. add a new `module.mount` in your `hugo.toml` file (in the same place where you reference bootstrap files):
```
## Search & sanitization
[[module.mounts]]
source = "node_modules/fuse.js/dist/fuse.min.js"
target = "static/js/fuse.min.js"
[[module.mounts]]
source = "node_modules/dompurify/dist/purify.min.js"
target = "static/js/purify.min.js"
```
2. add the following to your `hugo.toml` file:
```
home = ["HTML", "RSS", "JSON"]
```
Under the `outputs` area.
3. add a new page to your site, `content/search.md`:
```
---
title: "Search Results"
sitemap:
priority : 0.1
layout: "search"
---
```
You can now test the search by visiting `/search` in your site. It should load a page [similar to what is displayed in GitHub](https://github.com/zetxek/adritian-free-hugo-theme/pull/272). If that's not the case, please [open a ticket in the theme issue tracker](https://github.com/zetxek/adritian-free-hugo-theme/issues).
## v1.7.8
This version introduces the `footer` content type to customize the content at the bottom of the page, allowing for sections (such as `contact-section` or `newsletter-section`) as well as content.
A new shortcode (`text-section`) is introduced, to enable nicely wrapped text in some places such as the homepage or the footer.
## v1.7.4
The "home" item in the navigation (footer and header) is managed now in the configuration file (`hugo.toml`), instead of a hardcoded item (which couldn't be hidden).
See the PR [#249 for the content change](https://github.com/zetxek/adritian-free-hugo-theme/pull/249), in case you want to add the home item back (or edit its name/title or translations).
## v1.7.3
The theme has got rid of the `2x` strategy for responsive images.
They are now generated automatically from the large resolution picture.
This means that the partials using `2x` accept only one image now. There's a new attribute, `scale`, that defines how much larger the high-res image is compared to the smaller one.
See the [PR #244 for more context](https://github.com/zetxek/adritian-free-hugo-theme/pull/244), and the [#198 PR in the demo repo for the content changes only](https://github.com/zetxek/adritian-demo/pull/198).
## v1.7.0
This version introduces a brand new way of managing pages, and leveraging the existing the styles in different contexts, so they can be used in other pages than the homepage.
The main changes are:
- deprecation of `homepage.yml`. Stopped using some of its values (such as `.Site.Data.homepage.newsletter.enable`, given that the shortcodes can be rendered in any page, not only the home)
- introduction of numerous shortcodes, to replicate the same experience (in any page)
Aside from that:
- "education" comes now from its own content type
- fixed bugs where some buttons couldn't be hidden by not passing content
- deprecated dynamic content in `i18n` files - in favour of storing it in markdown files. For example, your page headings or descriptions won't be stored in these files anymore.
As usual, I have "dog-fed" the theme into my own website first. You can [see the PR showing the change](https://github.com/zetxek/adrianmoreno.info/pull/291) it took to migrate. The most significant file (that you can "copy-paste") is [home.md](https://github.com/zetxek/adrianmoreno.info/pull/291/files#diff-34cdd7812bb042723b4068c4df80283586271078662d619aa33f88e8e62d6fd2
).
You can also find a detailed PR for the demo site with [all the shortcodes in action](https://github.com/zetxek/adritian-demo/pull/182).
Note: I have tried to keep backwards compatibility up to a point. But **I recommend considering migrating your site to shortcodes**, if you want to keep updated with the upstream version. Support for `homepage.yml` will be less prioritary, and retired at some point, because of the complexity in mantaining multiple ways of passing content to the theme.
🛑 Alternatively, fix your theme to the last version `v1.6.1`, to prevent upgrading (losing the new features and improvements).
## v1.6.0
This version introduces the usage of [@zetxek/adritian-theme-helper](https://www.npmjs.com/package/adritian-theme-helper) to help initialize a site with the theme. This is not of much use to existing setups - but good to know.
Additionally, there are a number of [changes in HTML structure](https://github.com/zetxek/adritian-free-hugo-theme/pull/217/files) to ensure that the content is valid, including class renaming and change of element types.
Some of the changes are listed here in a an attempt to help you edit your custom CSS in case you depended on it:
- Prevent `p.lead` usage, preferring `div.lead` instead. This is done to prevent nested `<p>` elements or broken paragraphs, using a wrapping `div` instead.
- Fixed nested `main` elements in the `/blog` page, using instead `<main><div#main-content>`.
- Prevented duplicated usage of the `#experience` id, by renaming it to `#experience-single` in `layouts/partials/experience.html` and `#experience-list` in `layouts/shortcodes/experience-list.html`.
- Removed wrapping `<p>` element in `layouts/partials/testimonial.html`.
I hope this doesn't cause too much trouble in your existing sites - and allows for a smooth transition to `v1.6.0` 🤞
## v1.5.12
The options to control how the language and theme selectors are displayed in the header and footer have been refactored, to enable control over each placement individually (footer and header).
There are 3 placements that can be controlled. For the color selector, it's always available; for the languae selector, it's available if the site is multilingual.
The configuration syntax is as follows:
```
[params.languages.selector.disable]
footer = false
header = false
mobileHeader = false
[params.colorTheme.selector.disable]
footer = false
header = false
mobileHeader = false
```
The default value is assumed to be `false` for all placements, meaning that the selector is displayed in all placements by default.
## v1.5.7
In order to have the (optional) print improvements for the CV, you need to add the following to your `config.toml` file:
In the mounts section:
```
[[module.mounts]]
source = "node_modules/bootstrap-print-css/css/bootstrap-print.css"
target = "assets/css/bootstrap-print.css"
```
In the `params` section, under the `plugins` section, add the following:
```
[[params.plugins.css]]
URL = "css/bootstrap-print.css"
```
Note: this will add about 1.1Kb to the total size of the CSS loaded.
## v1.5.3
A new parameter, `showJobCard`, has been added to the `experience` section in the `config.toml` file.
This allows you to hide the job card on the experience page.
See the [related PR #271](https://github.com/zetxek/adritian-free-hugo-theme/pull/182) for more information.
## v1.5.2
Added a new (optional) parameter, "logo", to display an image in the header.
See the [related PR #179](https://github.com/zetxek/adritian-free-hugo-theme/pull/179) for more information.
## v1.5.1
The theme uses bootstrap as a dependency, installed from npm.
This requires some steps in the site:
1. copy the theme `package.json` to your site, and run `npm install`
2. add the following section to the `hugo.toml` configuration to your site:
```
[[module.mounts]]
source = "node_modules/bootstrap/scss"
target = "assets/scss/bootstrap"
[[module.mounts]]
source = "node_modules/bootstrap/dist/js"
target = "assets/js/bootstrap"
[[module.mounts]]
source = "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
target = "assets/js/vendor/bootstrap.bundle.min.js"
```
## v1.5.0
The theme has been updated to support Hugo modules.
This is now the recommended way to install the theme, as it allows for easier updates and contributions.
See https://github.com/zetxek/adrianmoreno.info/pull/270 for an example of how to update an existing site to switch from git submodule to Hugo module.
Some key steps:
- initialize the module in your site: `hugo mod init github.com/username/your-site`
- add the module to your `hugo.toml` file: `[[module.imports]] path = "github.com/zetxek/adritian-free-hugo-theme"`
- get the module: `hugo mod get -u`
To use a specific version of the theme, you can add the version to the module import: `[[module.imports]] path = "github.com/zetxek/adritian-free-hugo-theme@v2.0.0"`
To use an unpublished version of the theme, you can add the git reference to the `go.mod` file: `require github.com/zetxek/adritian-free-hugo-theme <any-git-reference>`, and then execute `hugo mod get -u`.
**Note**: if you use vercel to host your site, you will need to make sure that `go` is installed in the vercel build environment.
You can do this by adding the following to your `vercel.json` file: `"installCommand": "dnf -y install golang",`.
## v1.4.13
### Analytics section re-organized
In the [PR #121](https://github.com/zetxek/adritian-free-hugo-theme/pull/121), we moved the analytics config to a new section in the configuration for better extensibility and clarity.
Before:
```
vercelPageInsights = false
vercelAnalytics = false
[params.google_analytics]
code = "UA-XXXXX-Y"
enabled = false
[params.google_tag_manager]
code = "GTM-XXXXX"
enabled = false
```
After:
```
[params.analytics]
## Analytics parameters.
### Supported so far: Vercel (Page Insights, Analytics)
### And Google (Tag Manager, Analytics)
# controls vercel page insights - disabled by default
# to enable, just set to true
vercelPageInsights = false
vercelAnalytics = false
# google analytics and tag manager. to enable, set "enabled" to true
# and add the tracking code (UA-something for analytics, GTM-something for tag manager)
[params.analytics.googleAnalytics]
code = "UA-XXXXX-Y"
enabled = true
[params.analytics.googleTagManager]
code = "GTM-XXXXX"
enabled = false
```
See the theme's [`exampleSite` hugo.toml](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/exampleSite/hugo.toml) file for an example configuration.
### "Experience buttons" can be hidden
A new configuration option has been added to the experience buttons.
Before:
```
experience:
button:
enable: true
icon: "icon-linkedin"
## the url and text are localized, fill them in the i18n file
## keys: experience_button, experience_button_url
```
After:
```
experience:
enable: true
button:
enable: true
icon: "icon-linkedin"
## the url and text are localized, fill them in the i18n file
## keys: experience_button, experience_button_url
```
## v1.4.9
### Change 1: new translations keys
Added for the color switcher in the footer
- theme color switcher (`toggle_theme`) and themes (`theme_light`, `theme_dark`, `theme_auto`).
### Change 2: css files refactor
This version continues to align with Bootstrap extension capabilities.
You will need to change the import of CSS files in your `config.toml` file, in the `Plugins` section.
**Before:**
```
# CSS Plugins
[[params.plugins.css]]
URL = "css/main.css"
[[params.plugins.css]]
URL = "css/custom.css"
[[params.plugins.css]]
URL = "css/adritian-icons.css"
# JS Plugins
[[params.plugins.js]]
URL = "js/rad-animations.js"
[[params.plugins.js]]
URL = "js/sticky-header.js"
[[params.plugins.js]]
URL = "js/library/fontfaceobserver.js"
# SCSS Plugins
[[params.plugins.scss]]
URL = "scss/adritian.scss"
```
**After:**
See that the `main.css` file is gone from CSS:
```
# CSS Plugins
[[params.plugins.css]]
URL = "css/custom.css"
[[params.plugins.css]]
URL = "css/adritian-icons.css"
# JS Plugins
[[params.plugins.js]]
URL = "js/rad-animations.js"
[[params.plugins.js]]
URL = "js/sticky-header.js"
[[params.plugins.js]]
URL = "js/library/fontfaceobserver.js"
# SCSS Plugins
[[params.plugins.scss]]
URL = "scss/adritian.scss"
```
Also, added new parameters in `config.toml`:
```
# theme/color style
[params.colorTheme]
## the following configuration would disable automatic theme selection
# [params.colorTheme.auto]
# disable = true
# [params.colorTheme.forced]
# theme = "dark"
## the following parameter will disable theme override in the footer
# [params.colorTheme.selector.disable]
# footer = true
## by default we allow override AND automatic selection
```
## v1.4.5
This version has aligned more the custom CSS with Bootstrap's extension capabilities.
You will need to change the import of CSS files in your `config.toml` file, in the `Plugins` section.
**Before:**
```
# CSS Plugins
[[params.plugins.css]]
URL = "css/main.css"
[[params.plugins.css]]
URL = "css/adritian.css"
[[params.plugins.css]]
URL = "css/adritian-icons.css"
[[params.plugins.css]]
URL = "css/custom.css"
(...)
# SCSS Plugins
[[params.plugins.scss]]
URL = "scss/menu.scss"
[[params.plugins.scss]]
URL = "scss/bootstrap/bootstrap.scss"
```
**After:**
(note that `adritian.css` gets moved to the `scss` section, and it's not needed to import `bootstrap.scss` or `menu.scss` manually - as they are not included in `adritian.scss`)
```
# CSS Plugins
[[params.plugins.css]]
URL = "css/main.css"
[[params.plugins.css]]
URL = "css/custom.css"
[[params.plugins.css]]
URL = "css/adritian-icons.css"
# JS Plugins
[[params.plugins.js]]
URL = "js/rad-animations.js"
[[params.plugins.js]]
URL = "js/sticky-header.js"
[[params.plugins.js]]
URL = "js/library/fontfaceobserver.js"
# SCSS Plugins
[[params.plugins.scss]]
URL = "scss/adritian.scss"
```
## v1.4.0
This version switches from the legacy, "embedded" Boostrap based on v4.3.1 (from the [original codebase](https://github.com/radity/raditian-free-hugo-theme/blob/daa341d4156986787611a01d075ca94233ff4d3b/static/css/main.css)) to the Scss-based version, [v5.3.3](https://getbootstrap.com/docs/5.3) (ast per december'24).
This requires some small adjustments in the `config.toml` file.
### Add build stats
Add the following:
```
[build]
[build.buildStats]
disableClasses = false
disableIDs = false
disableTags = false
enable = true
```
### Add new `adritian.css` file
Add the following, under the `params.plugins.css` section:
```
[[params.plugins.css]]
URL = "css/adritian.css"
```
### ⚠️ Setup boostrap as Scss
Add the following, under the `params.plugins.scss` section:
```
[[params.plugins.scss]]
URL = "scss/bootstrap/bootstrap.scss"
```
See the contents of the [PR #94 in the demo site](https://github.com/zetxek/adritian-demo/pull/94/files) as an example.
## v1.3.4
In `config.toml`, replace
```
[[params.plugins.css]]
URL = "css/rad-icons.css"
```
by
```
[[params.plugins.css]]
URL = "css/adritian-icons.css"
```
In your config/content files, replace:
- `icon-linkedin-fill` by `icon-linkedin`
- `icon-smile-fill` by `icon-user`
- `icon-arrow-right` by `icon-circle-arrow-right`
- `icon-mail-fill` by `icon-email`
## v1.2.x to v1.3.x
In `v1.3.x`, the [support for internationalization was added](https://github.com/zetxek/adritian-free-hugo-theme/pull/78).
This means that some of the content previously managed in the `homepage.yaml` file is now managed in `i18n/{language}.yaml`.
For example:
- the main page title was managed in `homepage.yml`:`showcase.title`, now it's managed in `i18n/{language}.yaml`:`logo_text1`
- the page logo was managed in `hugo.toml`:`params.logo` (`text1` and `text2`), now it's managed in `i18n/{language}.yaml`:`logo_text1` and `logo_text2`.
The reason for the change is to allow overriding almost any string in the theme in different content translations.
+14
View File
@@ -0,0 +1,14 @@
+++
title = "{{ replace .Name "-" " " | title }}"
date = {{ .Date }}
draft = true
featured = false
weight = 100 # Lower weight appears first in featured sections
description = ""
[images]
featured_image = "" # Path to the featured image
tags = []
topics = []
+++
Add your content here.
+15
View File
@@ -0,0 +1,15 @@
---
date: '{{ .Date }}' # date in which the content is created - defaults to "today"
title: '{{ replace .File.ContentBaseName `-` ` ` | title }}'
draft: false # set to "true" if you want to hide the content
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
params:
logo:
src: "" # example: "images/clients/asgardia.png"
scale: 0.5
## The content is not used (yet). If you have ideas on how to use it,
## you can suggest it at https://github.com/zetxek/adritian-free-hugo-theme/discussions
---
+2
View File
@@ -0,0 +1,2 @@
+++
+++
+15
View File
@@ -0,0 +1,15 @@
---
date: '{{ .Date }}' # date in which the content is created - defaults to "today"
title: '{{ replace .File.ContentBaseName `-` ` ` | title }}'
draft: false # set to "true" if you want to hide the content
jobTitle: "" # job description/title. Fill-in
company: "" # name of the company you worked for. Fill-in
location: "" # place/city/country for the experience. Fill-in.
duration: "" # from-to, for example "2022-2024". Fill-in.
## For the content, you can use a title and a job description.
## For example:
# ### Fixing the world, one byte at a time
# The beginning of a great career.
#
---
+16
View File
@@ -0,0 +1,16 @@
---
date: '{{ .Date }}' # date in which the content is created - defaults to "today"
title: '{{ replace .File.ContentBaseName `-` ` ` | title }}'
draft: false # set to "true" if you want to hide the content
logo_x: "" # example: "images/clients/asgardia.png"
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
params:
button:
icon: ""
btnText: ""
URL: ""
image:
src: ""
## The content is used for the description of the project
---
+15
View File
@@ -0,0 +1,15 @@
---
date: '{{ .Date }}' # date in which the content is created - defaults to "today"
title: '{{ replace .File.ContentBaseName `-` ` ` | title }}'
draft: false # set to "true" if you want to hide the content
name: "" # place/city/country for the experience. Fill-in.
position: "" # from-to, for example "2022-2024". Fill-in.
params:
logo:
src: "" # example: "images/clients/asgardia.png"
## For the content, you can use markdown
##
---
@@ -0,0 +1,34 @@
.icon-threads:before { content: '\e800'; } /* '' */
.icon-bluesky:before { content: '\e801'; } /* '' */
.icon-x-twitter:before { content: '\e802'; } /* '' */
.icon-email:before { content: '\e803'; } /* '' */
.icon-tiktok:before { content: '\e804'; } /* '' */
.icon-search:before { content: '\e805'; } /* '' */
.icon-user:before { content: '\f007'; } /* '' */
.icon-table-list:before { content: '\f00b'; } /* '' */
.icon-download:before { content: '\f019'; } /* '' */
.icon-circle-info:before { content: '\f05a'; } /* '' */
.icon-square-twitter:before { content: '\f081'; } /* '' */
.icon-square-facebook:before { content: '\f082'; } /* '' */
.icon-linkedin:before { content: '\f08c'; } /* '' */
.icon-square-github:before { content: '\f092'; } /* '' */
.icon-circle-arrow-left:before { content: '\f0a8'; } /* '' */
.icon-circle-arrow-right:before { content: '\f0a9'; } /* '' */
.icon-circle-arrow-up:before { content: '\f0aa'; } /* '' */
.icon-circle-arrow-down:before { content: '\f0ab'; } /* '' */
.icon-quote-left:before { content: '\f10d'; } /* '' */
.icon-face-smile:before { content: '\f118'; } /* '' */
.icon-square-arrow-up-right:before { content: '\f14c'; } /* '' */
.icon-youtube:before { content: '\f167'; } /* '' */
.icon-square-xing:before { content: '\f169'; } /* '' */
.icon-stack-overflow:before { content: '\f16c'; } /* '' */
.icon-instagram:before { content: '\f16d'; } /* '' */
.icon-dribbble:before { content: '\f17d'; } /* '' */
.icon-behance:before { content: '\f1b4'; } /* '' */
.icon-file-pdf:before { content: '\f1c1'; } /* '' */
.icon-codepen:before { content: '\f1cb'; } /* '' */
.icon-yelp:before { content: '\f1e9'; } /* '' */
.icon-medium:before { content: '\f23a'; } /* '' */
.icon-meetup:before { content: '\f2e0'; } /* '' */
.icon-cloud-arrow-down:before { content: '\f381'; } /* '' */
File diff suppressed because one or more lines are too long
@@ -0,0 +1,89 @@
@font-face {
font-family: 'adritian-icons';
src: url('../fonts/adritian-icons.eot?62019798');
src: url('../fonts/adritian-icons.eot?62019798#iefix') format('embedded-opentype'),
url('../fonts/adritian-icons.woff2?62019798') format('woff2'),
url('../fonts/adritian-icons.woff?62019798') format('woff'),
url('../fonts/adritian-icons.ttf?62019798') format('truetype'),
url('../fonts/adritian-icons.svg?62019798#adritian-icons') format('svg');
font-weight: normal;
font-style: normal;
}
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
/*
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'adritian-icons';
src: url('../fonts/adritian-icons.svg?62019798#adritian-icons') format('svg');
}
}
*/
[class^="icon-"]:before, [class*=" icon-"]:before {
font-family: "adritian-icons";
font-style: normal;
font-weight: normal;
speak: never;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
margin-left: .2em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
.icon-threads:before { content: '\e800'; } /* '' */
.icon-bluesky:before { content: '\e801'; } /* '' */
.icon-x-twitter:before { content: '\e802'; } /* '' */
.icon-email:before { content: '\e803'; } /* '' */
.icon-tiktok:before { content: '\e804'; } /* '' */
.icon-search:before { content: '\e805'; } /* '' */
.icon-user:before { content: '\f007'; } /* '' */
.icon-table-list:before { content: '\f00b'; } /* '' */
.icon-download:before { content: '\f019'; } /* '' */
.icon-circle-info:before { content: '\f05a'; } /* '' */
.icon-square-twitter:before { content: '\f081'; } /* '' */
.icon-square-facebook:before { content: '\f082'; } /* '' */
.icon-linkedin:before { content: '\f08c'; } /* '' */
.icon-square-github:before { content: '\f092'; } /* '' */
.icon-circle-arrow-left:before { content: '\f0a8'; } /* '' */
.icon-circle-arrow-right:before { content: '\f0a9'; } /* '' */
.icon-circle-arrow-up:before { content: '\f0aa'; } /* '' */
.icon-circle-arrow-down:before { content: '\f0ab'; } /* '' */
.icon-quote-left:before { content: '\f10d'; } /* '' */
.icon-face-smile:before { content: '\f118'; } /* '' */
.icon-square-arrow-up-right:before { content: '\f14c'; } /* '' */
.icon-youtube:before { content: '\f167'; } /* '' */
.icon-square-xing:before { content: '\f169'; } /* '' */
.icon-stack-overflow:before { content: '\f16c'; } /* '' */
.icon-instagram:before { content: '\f16d'; } /* '' */
.icon-dribbble:before { content: '\f17d'; } /* '' */
.icon-behance:before { content: '\f1b4'; } /* '' */
.icon-file-pdf:before { content: '\f1c1'; } /* '' */
.icon-codepen:before { content: '\f1cb'; } /* '' */
.icon-yelp:before { content: '\f1e9'; } /* '' */
.icon-medium:before { content: '\f23a'; } /* '' */
.icon-meetup:before { content: '\f2e0'; } /* '' */
.icon-cloud-arrow-down:before { content: '\f381'; } /* '' */
+85
View File
@@ -0,0 +1,85 @@
/*
Animation example, for spinners
*/
.animate-spin {
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
-webkit-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
display: inline-block;
}
@-moz-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-webkit-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-o-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-ms-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
+1
View File
@@ -0,0 +1 @@
/* placeholder */

Some files were not shown because too many files have changed in this diff Show More