Eine schnelle Suche hier auf CSS-Tricks zeigt, wie viele verschiedene Ansätze es für Kalender gibt. Einige zeigen, wie CSS Grid das Layout effizient erstellen kann. Einige versuchen, reale Daten einzubeziehen. Einige verlassen sich auf ein Framework zur Verwaltung des Zustands.
Beim Erstellen einer Kalenderkomponente gibt es viele Überlegungen – weit mehr, als in den von mir verlinkten Artikeln behandelt werden. Wenn man darüber nachdenkt, sind Kalender voller Nuancen, von der Handhabung von Zeitzonen und Datumsformaten bis hin zur Lokalisierung und sogar der Sicherstellung, dass Daten von einem Monat zum nächsten fließen ... und das, bevor wir überhaupt auf Barrierefreiheit und zusätzliche Layout-Überlegungen eingehen, je nachdem, wo der Kalender angezeigt wird und so weiter.
Viele Entwickler fürchten das Date()-Objekt und bleiben bei älteren Bibliotheken wie moment.js. Aber obwohl es viele „Stolpersteine“ bei Datumsangaben und Formatierungen gibt, bietet JavaScript viele coole APIs und Hilfsmittel!

Ich möchte hier das Rad nicht neu erfinden, aber ich werde Ihnen zeigen, wie wir mit reinem JavaScript einen verdammt guten Kalender erstellen können. Wir werden uns auf Barrierefreiheit, die Verwendung semantischer Markup-Tags und bildschirmleserfreundlicher <time>-Tags konzentrieren – sowie auf Internationalisierung und Formatierung unter Verwendung der Intl.Locale, Intl.DateTimeFormat und Intl.NumberFormat-APIs.
Mit anderen Worten, wir erstellen einen Kalender ... nur ohne die zusätzlichen Abhängigkeiten, die man in einem solchen Tutorial typischerweise sehen würde, und mit einigen der Nuancen, die man normalerweise nicht sehen würde. Und dabei hoffe ich, dass Sie eine neue Wertschätzung für neuere Dinge gewinnen, die JavaScript tun kann, und gleichzeitig eine Vorstellung davon bekommen, welche Dinge mir durch den Kopf gehen, wenn ich so etwas zusammenstelle.
Zuerst die Benennung
Wie sollen wir unsere Kalenderkomponente nennen? In meiner Muttersprache würde sie „Kalenderelement“ heißen, also nehmen wir das und kürzen es zu „Kal-El“ ab – auch bekannt als Name von Superman auf dem Planeten Krypton.
Lassen Sie uns eine Funktion erstellen, um loszulegen
function kalEl(settings = {}) { ... }
Diese Methode rendert einen einzelnen Monat. Später rufen wir diese Methode aus [...Array(12).keys()] auf, um ein ganzes Jahr zu rendern.
Anfangsdaten und Internationalisierung
Eines der üblichen Dinge, die ein typischer Online-Kalender tut, ist das Hervorheben des aktuellen Datums. Erstellen wir also eine Referenz dafür
const today = new Date();
Als Nächstes erstellen wir ein „Konfigurationsobjekt“, das wir mit dem optionalen settings-Objekt der primären Methode zusammenführen werden
const config = Object.assign(
{
locale: (document.documentElement.getAttribute('lang') || 'en-US'),
today: {
day: today.getDate(),
month: today.getMonth(),
year: today.getFullYear()
}
}, settings
);
Wir prüfen, ob das Wurzelelement (<html>) ein lang-Attribut mit Locale-Informationen enthält; andernfalls greifen wir auf en-US zurück. Dies ist der erste Schritt zur Internationalisierung des Kalenders.
Wir müssen auch bestimmen, welcher Monat beim Rendern des Kalenders initial angezeigt werden soll. Deshalb haben wir das config-Objekt um das primäre date erweitert. Auf diese Weise verwenden wir die today-Referenz, falls kein Datum im settings-Objekt angegeben ist
const date = config.date ? new Date(config.date) : today;
Wir benötigen etwas mehr Informationen, um den Kalender korrekt nach Locale zu formatieren. Zum Beispiel wissen wir möglicherweise nicht, ob der erste Tag der Woche Sonntag oder Montag ist, abhängig von der Locale. Wenn wir die Information haben, großartig! Wenn nicht, aktualisieren wir sie mit der Intl.Locale API. Die API hat ein weekInfo Objekt, das eine firstDay-Eigenschaft zurückgibt, die uns ohne Probleme genau das liefert, wonach wir suchen. Wir können auch herausfinden, welche Tage der Woche dem weekend zugeordnet sind
if (!config.info) config.info = new Intl.Locale(config.locale).weekInfo || {
firstDay: 7,
weekend: [6, 7]
};
Auch hier erstellen wir Fallbacks. Der „erste Tag“ der Woche für en-US ist Sonntag, daher ist der Standardwert 7. Das ist etwas verwirrend, da die Methode getDay in JavaScript die Tage als [0-6] zurückgibt, wobei 0 Sonntag ist ... fragen Sie mich nicht, warum. Die Wochenenden sind Samstag und Sonntag, daher [6, 7].
Bevor es die Intl.Locale API und ihre weekInfo-Methode gab, war es ziemlich schwierig, einen internationalen Kalender ohne viele **Objekte und Arrays mit Informationen über jede Locale oder Region zu erstellen. Heutzutage ist das ein Kinderspiel. Wenn wir en-GB übergeben, gibt die Methode zurück
// en-GB
{
firstDay: 1,
weekend: [6, 7],
minimalDays: 4
}
In einem Land wie Brunei (ms-BN) sind Freitag und Sonntag das Wochenende
// ms-BN
{
firstDay: 7,
weekend: [5, 7],
minimalDays: 1
}
Sie fragen sich vielleicht, was diese minimalDays-Eigenschaft ist. Das ist die geringste Anzahl von Tagen, die in der ersten Woche eines Monats benötigt werden, um als volle Woche zu gelten. In einigen Regionen kann es nur ein Tag sein. In anderen volle sieben Tage.
Als Nächstes erstellen wir eine render-Methode innerhalb unserer kalEl-Methode
const render = (date, locale) => { ... }
Wir benötigen noch einige Daten, bevor wir etwas rendern
const month = date.getMonth();
const year = date.getFullYear();
const numOfDays = new Date(year, month + 1, 0).getDate();
const renderToday = (year === config.today.year) && (month === config.today.month);
Das letzte ist ein Boolean, das prüft, ob today im Monat vorkommt, den wir gerade rendern wollen.
Semantisches Markup
Wir werden gleich tiefer in das Rendering eintauchen. Aber zuerst möchte ich sicherstellen, dass die von uns eingerichteten Details mit semantischen HTML-Tags versehen sind. Wenn wir das von Anfang an richtig einrichten, profitieren wir von Anfang an von Barrierefreiheit.
Kalender-Wrapper
Zuerst haben wir den nicht-semantischen Wrapper: <kal-el>. Das ist in Ordnung, da es keinen semantischen <calendar>-Tag oder Ähnliches gibt. Wenn wir kein benutzerdefiniertes Element erstellen würden, wäre <article> wahrscheinlich das am besten geeignete Element, da der Kalender eigenständig auf einer Seite stehen könnte.
Monatsnamen
Das <time>-Element wird für uns wichtig sein, da es hilft, Daten in ein Format zu übersetzen, das Bildschirmleser und Suchmaschinen genauer und konsistenter interpretieren können. Zum Beispiel können wir hier „Januar 2023“ in unserem Markup darstellen
<time datetime="2023-01">January <i>2023</i></time>
Wochentagsnamen
Die Zeile über den Kalenderdaten, die die Namen der Wochentage enthält, kann knifflig sein. Es ist ideal, wenn wir die vollständigen Namen für jeden Tag ausschreiben können – z. B. Sonntag, Montag, Dienstag usw. – aber das kann viel Platz beanspruchen. Lassen Sie uns die Namen vorerst in einer <ol> abkürzen, wobei jeder Tag ein <li> ist
<ol>
<li><abbr title="Sunday">Sun</abbr></li>
<li><abbr title="Monday">Mon</abbr></li>
<!-- etc. -->
</ol>
Wir könnten mit CSS tricky werden, um das Beste aus beiden Welten zu erzielen. Zum Beispiel, wenn wir das Markup ein wenig so ändern würden
<ol>
<li>
<abbr title="S">Sunday</abbr>
</li>
</ol>
...erhalten wir standardmäßig die vollständigen Namen. Wir können dann den vollständigen Namen „verstecken“, wenn der Platz knapp wird, und stattdessen das title-Attribut anzeigen
@media all and (max-width: 800px) {
li abbr::after {
content: attr(title);
}
}
Aber wir gehen diesen Weg nicht, weil die Intl.DateTimeFormat API auch hier helfen kann. Darauf werden wir im nächsten Abschnitt eingehen, wenn wir das Rendering behandeln.
Tageszahlen
Jedes Datum im Kalenderraster erhält eine Zahl. Jede Zahl ist ein Listenelement (<li>) in einer geordneten Liste (<ol>), und der Inline-<time>-Tag umschließt die eigentliche Zahl.
<li>
<time datetime="2023-01-01">1</time>
</li>
Und obwohl ich noch keine Formatierung vornehmen möchte, weiß ich, dass ich auf irgendeine Weise die Datumszahlen formatieren möchte. Das ist so wie es ist möglich, aber ich möchte auch in der Lage sein, Wochentagszahlen anders als Wochenendzahlen zu formatieren, wenn ich es brauche. Deshalb werde ich data-* Attribute speziell dafür aufnehmen: data-weekend und data-today.
Wochennummern
Ein Jahr hat 52 Wochen, manchmal 53. Auch wenn es nicht sehr verbreitet ist, kann es schön sein, die Nummer einer bestimmten Woche im Kalender für zusätzlichen Kontext anzuzeigen. Ich mag es jetzt zu haben, auch wenn ich es nicht benutze. Aber wir werden es in diesem Tutorial auf jeden Fall benutzen.
Wir werden ein data-weeknumber-Attribut als Styling-Hook verwenden und es in das Markup für jedes Datum aufnehmen, das der erste Tag der Woche ist.
<li data-day="7" data-weeknumber="1" data-weekend="">
<time datetime="2023-01-08">8</time>
</li>
Rendering
Lassen Sie uns den Kalender auf eine Seite bringen! Wir wissen bereits, dass <kal-el> der Name unseres benutzerdefinierten Elements ist. Das Erste, was wir konfigurieren müssen, ist das Setzen der firstDay-Eigenschaft, damit der Kalender weiß, ob Sonntag oder ein anderer Tag der erste Tag der Woche ist.
<kal-el data-firstday="${ config.info.firstDay }">
Wir werden Template-Literale verwenden, um das Markup zu rendern. Um die Daten für ein internationales Publikum zu formatieren, werden wir die Intl.DateTimeFormat API verwenden, wieder mit der zuvor angegebenen locale.
Der Monat und das Jahr
Wenn wir month aufrufen, können wir festlegen, ob wir den Namen in voller Länge (z. B. Februar) oder die Kurzform (z. B. Feb.) verwenden möchten. Lassen Sie uns den Namen in voller Länge verwenden, da dies der Titel über dem Kalender ist
<time datetime="${year}-${(pad(month))}">
${new Intl.DateTimeFormat(
locale,
{ month:'long'}).format(date)} <i>${year}</i>
</time>
Wochentagsnamen
Für die Wochentage, die über dem Datumsraster angezeigt werden, benötigen wir sowohl die vollständigen Namen (z. B. „Sonntag“) als auch die abgekürzten Namen (z. B. „So.“). So können wir den „Kurznamen“ verwenden, wenn der Kalender wenig Platz hat
Intl.DateTimeFormat([locale], { weekday: 'long' })
Intl.DateTimeFormat([locale], { weekday: 'short' })
Lassen Sie uns eine kleine Hilfsmethode erstellen, die das Aufrufen jedes einzelnen erleichtert
const weekdays = (firstDay, locale) => {
const date = new Date(0);
const arr = [...Array(7).keys()].map(i => {
date.setDate(5 + i)
return {
long: new Intl.DateTimeFormat([locale], { weekday: 'long'}).format(date),
short: new Intl.DateTimeFormat([locale], { weekday: 'short'}).format(date)
}
})
for (let i = 0; i < 8 - firstDay; i++) arr.splice(0, 0, arr.pop());
return arr;
}
So rufen wir das im Template auf
<ol>
${weekdays(config.info.firstDay,locale).map(name => `
<li>
<abbr title="${name.long}">${name.short}</abbr>
</li>`).join('')
}
</ol>
Tageszahlen
Und schließlich die Tage, verpackt in einem <ol>-Element
${[...Array(numOfDays).keys()].map(i => {
const cur = new Date(year, month, i + 1);
let day = cur.getDay(); if (day === 0) day = 7;
const today = renderToday && (config.today.day === i + 1) ? ' data-today':'';
return `
<li data-day="${day}"${today}${i === 0 || day === config.info.firstDay ? ` data-weeknumber="${new Intl.NumberFormat(locale).format(getWeek(cur))}"`:''}${config.info.weekend.includes(day) ? ` data-weekend`:''}>
<time datetime="${year}-${(pad(month))}-${pad(i)}" tabindex="0">
${new Intl.NumberFormat(locale).format(i + 1)}
</time>
</li>`
}).join('')}
Lassen Sie uns das aufschlüsseln
- Wir erstellen ein „Dummy“-Array, basierend auf der Variablen „Anzahl der Tage“, das wir zur Iteration verwenden werden.
- Wir erstellen eine
day-Variable für den aktuellen Tag in der Iteration. - Wir beheben die Diskrepanz zwischen der
Intl.LocaleAPI undgetDay(). - Wenn der
daygleichtodayist, fügen wir eindata-*-Attribut hinzu. - Schließlich geben wir das
<li>-Element als String mit zusammengeführten Daten zurück. tabindex="0"macht das Element fokussierbar, wenn die Tastaturnavigation verwendet wird, nach allen positiven tabindex-Werten (Hinweis: Sie sollten niemals positive tabindex-Werte hinzufügen)
Um die Zahlen im datetime-Attribut zu „auffüllen“, verwenden wir eine kleine Hilfsmethode
const pad = (val) => (val + 1).toString().padStart(2, '0');
Wochennummer
Auch hier ist die „Wochennummer“ die Position einer Woche in einem 52-Wochen-Kalender. Dafür verwenden wir ebenfalls eine kleine Hilfsmethode
function getWeek(cur) {
const date = new Date(cur.getTime());
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
const week = new Date(date.getFullYear(), 0, 4);
return 1 + Math.round(((date.getTime() - week.getTime()) / 86400000 - 3 + (week.getDay() + 6) % 7) / 7);
}
Ich habe diese getWeek-Methode nicht geschrieben. Sie ist eine bereinigte Version von diesem Skript.
Und das war's! Dank der Intl.Locale, Intl.DateTimeFormat und Intl.NumberFormat APIs können wir nun einfach das lang-Attribut des Elements <html> ändern, um den Kontext des Kalenders basierend auf der aktuellen Region zu ändern

de-DE
fa-IR
zh-Hans-CN-u-nu-hanidecFormatierung des Kalenders
Sie erinnern sich vielleicht, wie alle Tage nur eine <ol> mit Listenelementen sind. Um diese zu einem lesbaren Kalender zu formatieren, tauchen wir in die wunderbare Welt von CSS Grid ein. Tatsächlich können wir das gleiche Grid aus einer Starter-Kalendervorlage hier auf CSS-Tricks wiederverwenden, aber mit einem kleinen Update mit der relationalen Pseudo-Klasse :is(), um den Code zu optimieren.
Beachten Sie, dass ich unterwegs konfigurierbare CSS-Variablen definiere (und sie mit ---kalel- präsentiere, um Konflikte zu vermeiden).
kal-el :is(ol, ul) {
display: grid;
font-size: var(--kalel-fz, small);
grid-row-gap: var(--kalel-row-gap, .33em);
grid-template-columns: var(--kalel-gtc, repeat(7, 1fr));
list-style: none;
margin: unset;
padding: unset;
position: relative;
}

Lassen Sie uns Rahmen um die Datumszahlen ziehen, um sie visuell zu trennen
kal-el :is(ol, ul) li {
border-color: var(--kalel-li-bdc, hsl(0, 0%, 80%));
border-style: var(--kalel-li-bds, solid);
border-width: var(--kalel-li-bdw, 0 0 1px 0);
grid-column: var(--kalel-li-gc, initial);
text-align: var(--kalel-li-tal, end);
}
Das Sieben-Spalten-Grid funktioniert gut, wenn der erste Tag des Monats auch der erste Tag der Woche für die ausgewählte Locale ist. Aber das ist eher die Ausnahme als die Regel. Meistens müssen wir den ersten Tag des Monats auf einen anderen Wochentag verschieben.

Erinnern Sie sich an all die zusätzlichen data-*-Attribute, die wir beim Schreiben unseres Markups definiert haben? Wir können diese nutzen, um zu aktualisieren, auf welche Grid-Spalte (--kalel-li-gc) die erste Datumszahl des Monats platziert wird
[data-firstday="1"] [data-day="3"]:first-child {
--kalel-li-gc: 1 / 4;
}
In diesem Fall spannen wir von der ersten Grid-Spalte bis zur vierten Grid-Spalte – was automatisch das nächste Element (Tag 2) in die fünfte Grid-Spalte „schiebt“ und so weiter.
Lassen Sie uns der „aktuellen“ Datumsangabe ein wenig Stil verleihen, damit sie auffällt. Das sind nur meine Stile. Sie können hier definitiv tun, was Sie möchten.
[data-today] {
--kalel-day-bdrs: 50%;
--kalel-day-bg: hsl(0, 86%, 40%);
--kalel-day-hover-bgc: hsl(0, 86%, 70%);
--kalel-day-c: #fff;
}
Ich mag die Idee, die Datumszahlen für Wochenenden anders als für Wochentage zu formatieren. Ich werde eine rötliche Farbe verwenden, um sie zu formatieren. Beachten Sie, dass wir die Pseudo-Klasse :not() verwenden können, um sie auszuwählen, während das aktuelle Datum unangetastet bleibt
[data-weekend]:not([data-today]) {
--kalel-day-c: var(--kalel-weekend-c, hsl(0, 86%, 46%));
}
Oh, und vergessen wir nicht die Wochennummern, die vor der ersten Datumszahl jeder Woche stehen. Wir haben dafür ein data-weeknumber-Attribut im Markup verwendet, aber die Zahlen werden erst angezeigt, wenn wir sie mit CSS sichtbar machen, was wir auf dem ::before-Pseudoelement tun können
[data-weeknumber]::before {
display: var(--kalel-weeknumber-d, inline-block);
content: attr(data-weeknumber);
position: absolute;
inset-inline-start: 0;
/* additional styles */
}
Technisch sind wir an diesem Punkt fertig! Wir können ein Kalenderraster rendern, das die Daten für den aktuellen Monat anzeigt, komplett mit Überlegungen zur Lokalisierung der Daten nach Locale und der Sicherstellung, dass der Kalender die richtige Semantik verwendet. Und alles, was wir verwendet haben, war reines JavaScript und CSS!
Aber lassen Sie uns das noch einen Schritt weiter gehen...
Rendern eines ganzen Jahres
Vielleicht müssen Sie ein volles Jahr von Daten anzeigen! Anstatt den aktuellen Monat zu rendern, möchten Sie vielleicht alle Monatsgitter des aktuellen Jahres anzeigen.
Nun, das Schöne an dem Ansatz, den wir verwenden, ist, dass wir die render-Methode beliebig oft aufrufen und lediglich die Ganzzahl ändern können, die den Monat in jeder Instanz identifiziert. Nennen wir sie 12 Mal basierend auf dem aktuellen Jahr.
so einfach wie das Aufrufen der render-Methode 12 Mal und das Ändern der Ganzzahl für month – i
[...Array(12).keys()].map(i =>
render(
new Date(date.getFullYear(),
i,
date.getDate()),
config.locale,
date.getMonth()
)
).join('')
Es ist wahrscheinlich eine gute Idee, einen neuen übergeordneten Wrapper für das gerenderte Jahr zu erstellen. Jedes Kalenderraster ist ein <kal-el>-Element. Nennen wir den neuen übergeordneten Wrapper <jor-el>, wobei Jor-El der Name von Kal-Els Vater ist.
<jor-el id="app" data-year="true">
<kal-el data-firstday="7">
<!-- etc. -->
</kal-el>
<!-- other months -->
</jor-el>
Wir können <jor-el> verwenden, um ein Grid für unsere Grids zu erstellen. So meta!
jor-el {
background: var(--jorel-bg, none);
display: var(--jorel-d, grid);
gap: var(--jorel-gap, 2.5rem);
grid-template-columns: var(--jorel-gtc, repeat(auto-fill, minmax(320px, 1fr)));
padding: var(--jorel-p, 0);
}
Finale Demo
Bonus: Konfetti-Kalender
Ich habe kürzlich ein ausgezeichnetes Buch namens Making and Breaking the Grid gelesen und bin auf dieses wunderschöne „Neujahrsplakat“ gestoßen

Ich dachte, wir könnten etwas Ähnliches tun, ohne etwas am HTML oder JavaScript zu ändern. Ich habe mir erlaubt, die vollständigen Monatsnamen und Zahlen anstelle von Wochentagsnamen aufzunehmen, um es leserlicher zu machen. Viel Spaß!
Es scheint einen Fehler bei der Wochenzählung in der finalen Demo zu geben: Immer wenn die ersten Tage eines Monats zur selben Woche gehören wie die letzten Tage des Vormonats, ist die Wochennummer für diese ersten paar Tage um eins zu hoch.
Listen sind möglicherweise nicht die Art und Weise, wie ein Nutzer von Bildschirmlesegeräten eine solche Konstruktion erwartet. Ein Grid, das die Traversierung mit den Pfeiltasten unterstützt, ist möglicherweise eher im Einklang mit den Erwartungen der Nutzer.
Sie werden auch das
title-Attribut für einabbr-Element nicht verwenden wollen.Für Dinge wie diese sollten Sie mit tatsächlicher assistiver Technologie testen und idealerweise mit täglichen Nutzern assistiver Technologie, um zu bestätigen, dass Ihre vorgeschlagene Komponente wie beabsichtigt funktioniert. Dies gilt insbesondere für Kalender, die
display-Manipulation verwenden, die die zugrunde liegende Semantik beeinträchtigen kann, undEs gibt viel in dem Beitrag, das Annahmen darüber macht, wie die Semantik von Elementen oder Attributen für Nutzer von Bildschirmlesegeräten vermittelt wird
Das
<abbr>-Element ist für Bildschirmlesegeräte nicht sehr nützlich, selbst mit demtitle-Attribut.Das
<time>-Element ist für maschinenlesbare Daten gedacht und bietet den Accessibility APIs nichts.Diese visuellen Kalenderraster sollten Tabellen sein (was Spalten- und Zeilenüberschriften sowie Tabellen-Navigationsbefehle ermöglicht).
Das
<time>-Element ist keine interaktive Steuerung, daher sollte es keinentabindexhaben; platzieren Sie es in einem<button>, wenn es den Fokus erhalten und eine Funktion ausführen soll (oder in einem Link, wenn es navigieren soll).Wenn Sie Bildschirmlesegeräte unterstützen möchten, sollte das aktuelle Datum
aria-currentverwenden.Danke für diesen Kommentar, ich würde ihn auch so sehen, wenn es nicht für Sie wäre.
Hallo,
Ich habe mich über Kalender informiert und Ihren Blogbeitrag gefunden. Ich muss sagen, er ist wirklich gut geschrieben.
Es gibt jedoch einige Dinge, die mich verwirren. Sie scheinen fast immer mit Zeitstempeln zu rechnen und Methoden wie
getDayzu verwenden, obwohl diese Methoden lokale Datumsangaben zurückgeben sollen; die UTC-Version ist z. B.getUTCDay. Diese klingt viel sicherer, da das Hinzufügen einiger Zeitstempel zu einer lokalen Zeit möglicherweise nicht das erwartete Ergebnis liefert (z. B. in Belgien werden einige lokale Zeiten zweimal im Jahr dargestellt, und andere sind unerreichbar). UTC scheint der Semantik von temporalen Koordinaten näher zu sein.In dieselbe Richtung habe ich beim Überprüfen des Endergebnisses versehentlich einen Fehler gefunden. Auf meinem Rechner sind die Wochen vom 31. Juli und 08. August beide als 31. Woche des Jahres markiert. Ich vermute, der Fehler kommt von einem Abgleich zwischen lokal und UTC.
Ich hoffe, dieser Kommentar hilft Ihnen, die Methoden zu klären und das Problem zu lösen! (Ich kann den Screenshot für den Fehler nicht posten, aber Sie können mich per E-Mail erreichen, wenn Sie ihn brauchen.)
Einen schönen Tag noch,
Justin
Großartiger Artikel, ich suchte nach einem Kalender für meine Website. Danke
Ich muss sagen, das ist eine erstaunliche Ressource, wenn es um CSS und Kalender geht. Sehr beeindruckt von der Flexibilität.