Files
robre 4b2300234c
Deploy Portfolio / deploy (push) Successful in 26s
Update: Aktuelle Änderungen
2026-05-10 13:05:00 +02:00

342 lines
14 KiB
TeX
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
% ============================================
% STEP 02: STYLING MIT TAILWIND & URBANIST
% ============================================
\section{Styling mit Tailwind CSS und Urbanist-Schriftart}
\label{sec:step02}
In diesem Schritt gestalten wir unsere Portfolio-Seite mit Tailwind CSS, binden eine eigene Schriftart ein und erstellen einen benutzerdefinierten Scrollbalken. Jede Zeile Code wird ausführlich erklärt.
\subsection{Schriftart Urbanist installieren}
Urbanist ist eine moderne, serifenlose Schriftart von Google Fonts mit 18 verschiedene Schnitten (Thin bis Black, jeweils normal und italic). Wir verwenden fünf Grundschnitte.
\textbf{Schritt 1: Schriftart-Dateien ins Projekt kopieren}
\begin{lstlisting}[language=Bash, caption={Schriftart-Dateien ins Public-Verzeichnis kopieren}]
cd ~/projects/portfolio
cp -r Urbanist apps/web/public/Urbanist
\end{lstlisting}
\textbf{Warum \texttt{public/}?}
\begin{itemize}
\item Alles im \texttt{public/}-Ordner wird von Vite unverändert übernommen
\item Dateien sind unter \texttt{/Urbanist/static/...} im Browser erreichbar
\item Kein Import nötig direkter Pfad in CSS
\end{itemize}
\subsection{Index.css Das Herzstück des Stylings}
Die Datei \texttt{apps/web/src/index.css} vereint Tailwind, Schriftarten und Custom-Styles. Hier jede Zeile im Detail erklärt.
\subsubsection{Tailwind Import}
\begin{lstlisting}[language=CSS, caption={Tailwind CSS importieren}]
@import "tailwindcss";
\end{lstlisting}
\textbf{Erklärung:}
\begin{itemize}
\item \texttt{@import} CSS-Regel zum Importieren anderer Stylesheets
\item \texttt{"tailwindcss"} Lädt alle Tailwind-Basis-Styles, Komponenten und Utilities
\item In Tailwind 4 \textbf{kein} \texttt{@tailwind base/components/utilities} mehr!
\item Diese einzige Zeile ersetzt Hunderte von handgeschriebenen CSS-Zeilen
\end{itemize}
\subsubsection{Schriftart-Definitionen (5 Schnitte)}
\begin{lstlisting}[language=CSS, caption={Urbanist Regular (400)}]
@font-face {
font-family: "Urbanist";
src: url("/Urbanist/static/Urbanist-Regular.ttf") format("truetype");
font-weight: 400;
font-style: normal;
font-display: swap;
}
\end{lstlisting}
\textbf{Zeile für Zeile:}
\begin{itemize}
\item \texttt{@font-face} CSS-At-Regel zum Definieren einer eigenen Schriftart
\item \texttt{font-family: "Urbanist"} Der Name, unter dem wir die Schrift später verwenden (z.B. in \texttt{font-family: var(--font-display)})
\item \texttt{src: url("/Urbanist/...")} Pfad zur Schriftdatei. Beginnt mit \texttt{/}, ist also relativ zur Domain-Root
\item \texttt{format("truetype")} Sagt dem Browser, welches Format die Datei hat (.ttf = TrueType)
\item \texttt{font-weight: 400} Diesen Schnitt für normale Textstärke verwenden (400 = Regular)
\item \texttt{font-style: normal} Kein Kursiv (italic wäre \texttt{font-style: italic})
\item \texttt{font-display: swap} WICHTIG! Zeigt sofort Text in einer Ersatzschrift an und tauscht sie aus, sobald Urbanist geladen ist. Verhindert "Flash of Invisible Text" (FOIT)
\end{itemize}
\textbf{Die 5 definierten Schnitte:}
\begin{enumerate}
\item \texttt{Urbanist-Light.ttf} Gewicht 300 (dünn, für Fließtext)
\item \texttt{Urbanist-Regular.ttf} Gewicht 400 (normal, Standard)
\item \texttt{Urbanist-Medium.ttf} Gewicht 500 (halbfett)
\item \texttt{Urbanist-SemiBold.ttf} Gewicht 600 (fast fett)
\item \texttt{Urbanist-Bold.ttf} Gewicht 700 (fett, für Überschriften)
\end{enumerate}
\subsubsection{Tailwind Theme}
\begin{lstlisting}[language=CSS, caption={Tailwind Custom Theme}]
@theme {
--font-display: "Urbanist", sans-serif;
--breakpoint-3xl: 1920px;
--color-primary: #8dff69;
}
\end{lstlisting}
\textbf{Zeile für Zeile:}
\begin{itemize}
\item \texttt{@theme} Tailwind 4 Direktive zum Erweitern/Überschreiben des Standard-Themes
\item \texttt{--font-display: "Urbanist", sans-serif} Definiert eine neue Schrift-Klasse \texttt{font-display}. \texttt{sans-serif} ist der Fallback, falls Urbanist nicht lädt
\item \texttt{--breakpoint-3xl: 1920px} Neuer Breakpoint für sehr große Bildschirme. Nutzbar als \texttt{3xl:text-7xl}
\item \texttt{--color-primary: \#8dff69} Definiert eine neue Farbe \texttt{primary} (helles Grün). Nutzbar als \texttt{text-primary}, \texttt{bg-primary}, \texttt{border-primary}, etc.
\end{itemize}
\textbf{Verwendung der Custom-Properties in Tailwind:}
\begin{itemize}
\item \texttt{font-display} in \texttt{font-family: var(--font-display)}
\item \texttt{3xl} als Breakpoint: \texttt{3xl:grid-cols-4}
\item \texttt{primary} als Farbe: \texttt{text-primary}, \texttt{bg-primary/50}, \texttt{border-primary}
\end{itemize}
\subsubsection{Base-Layer (globale Stile)}
\begin{lstlisting}[language=CSS, caption={Globale Stile}]
@layer base {
html {
font-family: var(--font-display);
scroll-behavior: smooth;
}
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #000000;
color: #ffffff;
overflow-x: hidden;
}
}
\end{lstlisting}
\textbf{Zeile für Zeile:}
\begin{itemize}
\item \texttt{@layer base} Tailwinds "base"-Layer. Styles hier haben niedrigste Spezifität und können von Utilities überschrieben werden
\item \texttt{font-family: var(--font-display)} Setzt Urbanist als Standardschrift für die gesamte Seite
\item \texttt{scroll-behavior: smooth} Sanftes Scrollen bei Ankerlinks (z.B. \texttt{\#projekte})
\item \texttt{-webkit-font-smoothing: antialiased} Glättet Schrift auf macOS/iOS (WebKit-Browser)
\item \texttt{-moz-osx-font-smoothing: grayscale} Gleiches für Firefox auf macOS
\item \texttt{background-color: \#000000} Schwarzer Hintergrund
\item \texttt{color: \#ffffff} Weiße Schrift als Standard
\item \texttt{overflow-x: hidden} Verhindert horizontales Scrollen (wichtig für Animationen, die über den Rand hinausgehen)
\end{itemize}
\subsubsection{Custom Scrollbar (WebKit)}
\begin{lstlisting}[language=CSS, caption={Scrollbar für Chrome, Edge, Safari}]
::-webkit-scrollbar {
width: 16px;
}
::-webkit-scrollbar-track {
background: #1a1a1a;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #8dff69, #6fe047);
border-radius: 8px;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #a8ff8d, #5bc92e);
}
\end{lstlisting}
\textbf{Zeile für Zeile:}
\begin{itemize}
\item \texttt{::-webkit-scrollbar} Pseudo-Element für die gesamte Scrollbar (Breite, Hintergrund)
\item \texttt{width: 16px} Breite der Scrollbar. Größer = dickerer Balken
\item \texttt{::-webkit-scrollbar-track} Der "Hintergrund" der Scrollbar (die Schiene)
\item \texttt{background: \#1a1a1a} Dunkelgrauer Track (fast schwarz)
\item \texttt{::-webkit-scrollbar-thumb} Der bewegliche "Griff" der Scrollbar
\item \texttt{linear-gradient(180deg, \#8dff69, \#6fe047)} Farbverlauf von hellgrün (oben) nach dunkelgrün (unten). \texttt{180deg} = von oben nach unten
\item \texttt{border-radius: 8px} Abgerundete Ecken am Griff
\item \texttt{::-webkit-scrollbar-thumb:hover} Wenn die Maus über dem Griff ist
\item \texttt{\#a8ff8d, \#5bc92e} Hellere Grüntöne beim Hover für visuelles Feedback
\end{itemize}
\subsubsection{Firefox Scrollbar}
\begin{lstlisting}[language=CSS, caption={Scrollbar für Firefox}]
* {
scrollbar-width: auto;
scrollbar-color: #6fe047 #1a1a1a;
}
\end{lstlisting}
\textbf{Erklärung:}
\begin{itemize}
\item Firefox unterstützt \texttt{::-webkit-scrollbar} \textbf{nicht}
\item \texttt{scrollbar-width: auto} Normale Breite (Alternative: \texttt{thin})
\item \texttt{scrollbar-color: GRIFF SCHIENE} Erste Farbe = Griff, zweite Farbe = Schiene
\end{itemize}
\subsubsection{Hilfsklasse: Scrollbar ausblenden}
\begin{lstlisting}[language=CSS, caption={Hilfsklasse für Karussells}]
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
\end{lstlisting}
Für horizontale Karussells der Inhalt scrollt, aber die Scrollbar ist unsichtbar.
\subsection{App.tsx Die React-Komponente erklärt}
\begin{lstlisting}[language=TypeScript, caption={Vollständige App.tsx mit Zeilen-Erklärung}]
function App() {
return (
// Äußerster Container
// min-h-screen: mindestens Bildschirmhöhe (wichtig für zentrierte Inhalte)
// bg-black: schwarzer Hintergrund
// text-white: weiße Schrift als Basis
// p-8: 32px Innenabstand auf allen Seiten
<div className="min-h-screen bg-black text-white p-8">
{/* Zentrierte Content-Box */}
{/* text-center: zentriert Text und Inline-Elemente */}
{/* max-w-2xl: maximale Breite 672px */}
{/* mx-auto: automatischer horizontaler Margin = zentriert die Box */}
{/* pt-20: 80px Abstand nach oben (Padding-Top) */}
<div className="text-center max-w-2xl mx-auto pt-20">
{/* Kategorie-Badge: Kleine Beschriftung ÜBER der Überschrift */}
{/* text-primary: verwendet unsere Custom-Farbe #8dff69 */}
{/* text-sm: kleinere Schriftgröße (14px) */}
{/* font-medium: halbfette Schrift (Gewicht 500) */}
{/* tracking-widest: sehr breite Buchstabenabstände */}
{/* uppercase: GROSSBUCHSTABEN */}
<span className="text-primary text-sm font-medium tracking-widest uppercase">
Full-Stack Entwickler
</span>
{/* Hauptüberschrift: Dein Name */}
{/* text-7xl: sehr große Schrift (72px) */}
{/* font-bold: fette Schrift (Gewicht 700) */}
{/* mt-4: 16px Abstand nach oben */}
{/* mb-6: 24px Abstand nach unten */}
{/* leading-tight: enger Zeilenabstand (1.25) */}
<h1 className="text-7xl font-bold mt-4 mb-6 leading-tight">
Robert Bretz
</h1>
{/* Beschreibungstext */}
{/* text-xl: etwas größerer Text (20px) */}
{/* text-gray-400: graue Schrift (gedämpfter als weiß) */}
{/* max-w-lg: maximale Breite 512px (schmaler = besser lesbar) */}
{/* leading-relaxed: lockerer Zeilenabstand (1.625) */}
<p className="text-xl text-gray-400 max-w-lg mx-auto leading-relaxed">
Ich baue moderne Webanwendungen mit React, .NET und Docker.
Minimalistisch, schnell und zuverlässig.
</p>
{/* Button-Gruppe */}
{/* mt-10: 40px Abstand nach oben */}
{/* flex: Flexbox-Layout */}
{/* gap-4: 16px Abstand zwischen den Buttons */}
{/* justify-center: Buttons horizontal zentrieren */}
<div className="mt-10 flex gap-4 justify-center">
{/* Button "Projekte" */}
{/* px-6: 24px horizontaler Innenabstand */}
{/* py-3: 12px vertikaler Innenabstand */}
{/* bg-primary: Hintergrund in Custom-Farbe #8dff69 */}
{/* text-black: schwarze Schrift auf grünem Grund */}
{/* font-semibold: halbfette Schrift (Gewicht 600) */}
{/* rounded-full: komplett runde Ecken (Pillenform) */}
{/* hover:opacity-80: beim Hover auf 80% Deckkraft */}
{/* transition: sanfter Übergang */}
<a href="#" className="px-6 py-3 bg-primary text-black font-semibold rounded-full hover:opacity-80 transition">
Projekte
</a>
{/* Button "Kontakt" */}
{/* border border-gray-700: dunkelgrauer Rahmen statt Hintergrund */}
{/* text-gray-300: hellgraue Schrift */}
{/* hover:border-primary: Rahmen wird grün beim Hover */}
{/* hover:text-primary: Schrift wird grün beim Hover */}
<a href="#" className="px-6 py-3 border border-gray-700 text-gray-300 font-semibold rounded-full hover:border-primary hover:text-primary transition">
Kontakt
</a>
</div>
</div>
{/* SCROLL-TEST: Erzwingt Scrollen für den Custom Scrollbar */}
{/* mt-20: 80px Abstand nach oben */}
{/* space-y-8: 32px Abstand zwischen den Kind-Elementen */}
{/* max-w-2xl: gleiche maximale Breite wie oben */}
<div className="mt-20 space-y-8 max-w-2xl mx-auto">
{/* JavaScript: Erstelle 5 Test-Blöcke */}
{/* [...Array(5)]: Array mit 5 leeren Plätzen */}
{/* .map((_, i): für jedes Element, _ = ignorierter Wert, i = Index (0-4) */}
{[...Array(5)].map((_, i) => (
<div
key={i}
className="h-64 bg-gray-900 rounded-xl flex items-center justify-center text-gray-500 text-2xl"
>
Sektion {i + 1}
</div>
))}
</div>
</div>
);
}
export default App;
\end{lstlisting}
\subsection{Verwendete Tailwind-Klassen Cheat Sheet}
\begin{table}[h]
\centering
\caption{Alle verwendeten Tailwind-Klassen und ihre CSS-Entsprechung}
\begin{tabular}{@{}llp{5cm}@{}}
\toprule
\textbf{Klasse} & \textbf{CSS-Entsprechung} & \textbf{Erklärung} \\
\midrule
\texttt{min-h-screen} & \texttt{min-height: 100vh} & Mindestens Bildschirmhöhe \\
\texttt{bg-black} & \texttt{background: \#000} & Schwarzer Hintergrund \\
\texttt{text-white} & \texttt{color: \#fff} & Weiße Schrift \\
\texttt{p-8} & \texttt{padding: 2rem} & 32px Innenabstand \\
\texttt{text-center} & \texttt{text-align: center} & Text zentrieren \\
\texttt{max-w-2xl} & \texttt{max-width: 42rem} & Max 672px breit \\
\texttt{mx-auto} & \texttt{margin-left/right: auto} & Horizontal zentrieren \\
\texttt{pt-20} & \texttt{padding-top: 5rem} & 80px oben \\
\bottomrule
\end{tabular}
\end{table}
\subsection{Scrollbalken-Debugging}
\textbf{Problem: "Ich sehe den Scrollbalken nicht!"}
\textbf{Ursache:} Der Scrollbalken erscheint nur, wenn die Seite tatsächlich scrollbar ist. Bei kurzen Inhalten (nur Name + Buttons) passt alles auf eine Bildschirmseite kein Scrollbalken nötig.
\textbf{Lösungen:}
\begin{enumerate}
\item Browser-Fenster vertikal verkleinern
\item Genug Test-Inhalt einfügen (5 große Divs wie im Code oben)
\item Mit \texttt{overflow-y: scroll} einen permanenten Scrollbalken erzwingen
\end{enumerate}
\subsection{Zusammenfassung}
In diesem Schritt haben wir:
\begin{itemize}
\item Die Schriftart Urbanist mit 5 Schnitten eingebunden
\item Ein Tailwind Custom-Theme mit eigener Farbe und Breakpoint definiert
\item Globale Stile im \texttt{@layer base} gesetzt
\item Einen benutzerdefinierten Scrollbalken mit Farbverlauf erstellt
\item Jede Zeile der \texttt{App.tsx} und \texttt{index.css} ausführlich dokumentiert
\end{itemize}