17 Commits
Author SHA1 Message Date
aziis98 74ed415980 fixed half width case 2025-06-26 10:53:56 +02:00
aziis98 cc12c4fc18 fixed link 2025-06-26 10:47:02 +02:00
Luca Lombardo eb667f7f04 feat: update title and heading for media calculator announcement 2025-06-26 10:43:58 +02:00
Luca Lombardo 575a37accc feat: update MediaPesataApp to cap bonus lodi based on student type and add new media calculator documentation 2025-06-26 09:59:38 +02:00
aziis98 bbad670baa style changes 2025-06-25 18:23:21 +02:00
Luca Lombardo 4c19ffdd9f fix: update course data comments and add new courses with passFailOnly property 2025-06-18 19:06:33 +02:00
Luca Lombardo 15c2b44fcf feat: add passFailOnly property to 'Laboratorio computazionale' course 2025-06-18 18:45:23 +02:00
Luca Lombardo 34597fe9f8 feat: implement localStorage functionality for MediaPesataApp and remove media.astro page 2025-06-18 16:29:48 +02:00
Luca Lombardo 4780133019 fix: restore 'Calcolo Media' link in header navigation 2025-06-18 16:05:57 +02:00
Luca Lombardo 55352af0f2 Add media pesata page and corresponding styles
- Created a new page for calculating weighted averages and graduation votes (`media-pesata.astro`) with a layout and introductory text.
- Implemented a script to initialize the media pesata application.
- Added CSS styles for the media pesata page and its components.
- Updated existing media page (`media.astro`) to include similar structure and functionality.
- Introduced error handling for the application initialization script.
- Established a comprehensive CSS file for the media pesata application, including styles for various UI elements and responsive design.
2025-06-18 16:05:09 +02:00
aziis98 b595169e3b chore: final code re-organization after the css refactoring 2025-06-06 01:17:03 +02:00
aziis98 d75ccecdf2 style: another minor image tweak 2025-06-06 01:13:21 +02:00
aziis98 a694817c28 style: minor page card tweaks 2025-06-06 00:58:09 +02:00
aziis98 a2125bdab7 major scss to css refactor, removed pageTags and simplified per page css 2025-06-06 00:51:08 +02:00
aziis98 ddca56e868 minor latex update 2025-06-05 22:21:19 +02:00
aziis98 da7f526cb0 aggiunte alcune domande di teoria dei nodi 2025-06-03 14:38:23 +02:00
aziis98 370f8204a0 Merge pull request 'fixed order of news on the main page' (#17) from i.serdyuk/website:main into main
Reviewed-on: phc/website#17
2025-05-19 17:36:05 +00:00
43 changed files with 3791 additions and 1448 deletions
+5 -1
View File
@@ -2,6 +2,7 @@ import { defineConfig } from 'astro/config'
import preact from '@astrojs/preact'
import mdx from '@astrojs/mdx'
import remarkMath from 'remark-math'
import yaml from '@rollup/plugin-yaml'
@@ -14,6 +15,7 @@ export default defineConfig({
port: 3000,
},
markdown: {
remarkPlugins: [remarkMath],
shikiConfig: {
theme: 'github-light',
},
@@ -22,7 +24,9 @@ export default defineConfig({
preact({
compat: true,
}),
mdx(),
mdx({
remarkPlugins: [remarkMath],
}),
],
output: 'static',
})
BIN
View File
Binary file not shown.
+765
View File
@@ -0,0 +1,765 @@
import { useState, useEffect } from 'preact/hooks'
import { render } from 'preact'
// Tipi per la gestione dei dati
type TipoStudente = 'triennale' | 'magistrale'
interface Corso {
nome: string
anno: '1' | '2' | '3' | 'M' | 'istituzioni'
cfu: number
passFailOnly?: boolean // Per materie senza voto (pass/fail)
}
interface CorsoSelezionato {
id: string
nome: string
cfu: number
voto: number | null
lode: boolean
passFailOnly?: boolean // Per materie senza voto (pass/fail)
}
interface CorsoCustom {
nome: string
cfu: number
}
// Dati dei corsi aggiornati dalla tabella ufficiale
const CORSI_DISPONIBILI: Corso[] = [
// Primo Anno
{ nome: 'Analisi matematica 1', anno: '1', cfu: 15 },
{ nome: 'Aritmetica', anno: '1', cfu: 9 },
{ nome: 'Fisica I con laboratorio', anno: '1', cfu: 9 },
{ nome: 'Fondamenti di programmazione con laboratorio', anno: '1', cfu: 9 },
{ nome: 'Geometria 1', anno: '1', cfu: 15 },
{ nome: 'Laboratorio di introduzione alla matematica computazionale', anno: '1', cfu: 6, passFailOnly: true },
{ nome: 'Laboratorio di comunicazione mediante calcolatore', anno: '1', cfu: 3, passFailOnly: true },
// Secondo Anno
{ nome: 'Algebra 1', anno: '2', cfu: 6 },
{ nome: 'Algoritmi e strutture dati', anno: '2', cfu: 6 },
{ nome: 'Analisi matematica 2', anno: '2', cfu: 12 },
{ nome: 'Analisi numerica con laboratorio', anno: '2', cfu: 9 },
{ nome: 'Elementi di probabilità e statistica', anno: '2', cfu: 6 },
{ nome: 'Geometria 2', anno: '2', cfu: 12 },
{ nome: 'Inglese scientifico', anno: '2', cfu: 6, passFailOnly: true },
{ nome: 'Laboratorio didattico di matematica computazionale', anno: '2', cfu: 3, passFailOnly: true },
// Terzo Anno
{ nome: 'Algebra 2', anno: '3', cfu: 6 },
{ nome: 'Analisi matematica 3', anno: '3', cfu: 6 },
{ nome: 'Analisi reale', anno: '3', cfu: 6 },
{ nome: 'Calcolo scientifico', anno: '3', cfu: 6 },
{ nome: 'Elementi di analisi complessa', anno: '3', cfu: 6 },
{ nome: 'Elementi di calcolo delle variazioni', anno: '3', cfu: 6 },
{ nome: 'Elementi di geometria algebrica', anno: '3', cfu: 6 },
{ nome: 'Elementi di meccanica celeste', anno: '3', cfu: 6 },
{ nome: 'Elementi di teoria degli insiemi', anno: '3', cfu: 6 },
{ nome: 'Elementi di teoria delle rappresentazioni', anno: '3', cfu: 6 },
{ nome: 'Elementi di topologia algebrica', anno: '3', cfu: 6 },
{ nome: 'Equazioni alle derivate parziali', anno: '3', cfu: 6 },
{ nome: 'Fisica II', anno: '3', cfu: 9 },
{ nome: 'Fisica III', anno: '3', cfu: 6 },
{ nome: 'Geometria e topologia differenziale', anno: '3', cfu: 6 },
{ nome: 'Gruppi e rappresentazioni', anno: '3', cfu: 6 },
{ nome: 'Laboratorio computazionale', anno: '3', cfu: 6 },
{ nome: 'Linguaggi di programmazione con laboratorio', anno: '3', cfu: 9 },
{ nome: 'Logica matematica', anno: '3', cfu: 6 },
{ nome: 'Matematiche elementari da un punto di vista superiore: aritmetica', anno: '3', cfu: 6 },
{ nome: 'Matematiche elementari da un punto di vista superiore: geometria', anno: '3', cfu: 6 },
{ nome: 'Meccanica razionale', anno: '3', cfu: 6 },
{ nome: 'Metodi numerici per equazioni differenziali ordinarie', anno: '3', cfu: 6 },
{ nome: 'Metodi topologici in analisi globale', anno: '3', cfu: 6 },
{ nome: 'Ottimizzazione non lineare', anno: '3', cfu: 6 },
{ nome: 'Probabilità', anno: '3', cfu: 6 },
{ nome: 'Ricerca operativa', anno: '3', cfu: 6 },
{ nome: 'Sistemi dinamici', anno: '3', cfu: 6 },
{ nome: 'Spazi di Sobolev', anno: '3', cfu: 6 },
{ nome: 'Statistica matematica', anno: '3', cfu: 6 },
{ nome: 'Storia della matematica', anno: '3', cfu: 6 },
{ nome: 'Teoria algebrica dei numeri 1', anno: '3', cfu: 6 },
{ nome: 'Teoria dei campi e teoria di Galois', anno: '3', cfu: 6 },
{ nome: 'Teoria dei numeri elementare', anno: '3', cfu: 6 },
{ nome: 'Teoria della misura', anno: '3', cfu: 6 },
// Istituzioni (Magistrale)
{ nome: 'Istituzioni di algebra', anno: 'istituzioni', cfu: 11 },
{ nome: 'Istituzioni di analisi matematica', anno: 'istituzioni', cfu: 11 },
{ nome: 'Istituzioni di analisi numerica', anno: 'istituzioni', cfu: 11 },
{ nome: 'Istituzioni di didattica della matematica', anno: 'istituzioni', cfu: 11 },
{ nome: 'Istituzioni di fisica matematica', anno: 'istituzioni', cfu: 11 },
{ nome: 'Istituzioni di geometria', anno: 'istituzioni', cfu: 11 },
{ nome: 'Istituzioni di probabilità', anno: 'istituzioni', cfu: 11 },
// Materie a scelta (Magistrale)
{ nome: '4-varietà', anno: 'M', cfu: 6 },
{ nome: 'Algebra superiore A', anno: 'M', cfu: 6 },
{ nome: 'Algebre e gruppi di Lie', anno: 'M', cfu: 6 },
{ nome: 'Analisi armonica', anno: 'M', cfu: 6 },
{ nome: 'Analisi complessa A', anno: 'M', cfu: 6 },
{ nome: 'Analisi complessa B', anno: 'M', cfu: 6 },
{ nome: 'Analisi convessa', anno: 'M', cfu: 6 },
{ nome: 'Analisi dei dati', anno: 'M', cfu: 6 },
{ nome: 'Analisi non standard', anno: 'M', cfu: 6 },
{ nome: 'Analisi reale', anno: 'M', cfu: 6 },
{ nome: 'Analisi su spazi gaussiani', anno: 'M', cfu: 6 },
{ nome: 'Analisi superiore', anno: 'M', cfu: 6 },
{ nome: 'Analisi superiore A', anno: 'M', cfu: 6 },
{ nome: 'Analisi superiore B', anno: 'M', cfu: 6 },
{ nome: 'Aspetti matematici nella computazione quantistica', anno: 'M', cfu: 6 },
{ nome: 'Calcolo delle variazioni B', anno: 'M', cfu: 6 },
{ nome: 'Calcolo della variazioni A', anno: 'M', cfu: 6 }, // Variante nome
{ nome: 'Combinatoria algebrica', anno: 'M', cfu: 6 },
{ nome: 'Complementi di analisi funzionale', anno: 'M', cfu: 6 },
{ nome: 'Complementi di meccanica razionale', anno: 'M', cfu: 6 },
{ nome: 'Crittografia post-quantistica', anno: 'M', cfu: 6 },
{ nome: 'Curve ellittiche', anno: 'M', cfu: 6 },
{ nome: 'Determinazione orbitale', anno: 'M', cfu: 6 },
{ nome: 'Didattica della matematica e nuove tecnologie', anno: 'M', cfu: 6 },
{ nome: 'Dinamica del sistema solare', anno: 'M', cfu: 6 },
{ nome: 'Dinamica iperbolica', anno: 'M', cfu: 6 },
{ nome: 'Dinamica olomorfa', anno: 'M', cfu: 6 },
{ nome: 'Elementi di calcolo in gruppi omogenei', anno: 'M', cfu: 6 },
{ nome: 'Equazioni della fluidodinamica', anno: 'M', cfu: 6 },
{ nome: 'Equazioni differenziali stocastiche e applicazioni', anno: 'M', cfu: 6 },
{ nome: 'Equazioni ellittiche', anno: 'M', cfu: 6 },
{ nome: 'Finanza matematica', anno: 'M', cfu: 6 },
{ nome: 'Fisica matematica', anno: 'M', cfu: 6 },
{ nome: 'Forme modulari', anno: 'M', cfu: 6 },
{ nome: 'Geometria algebrica B', anno: 'M', cfu: 6 },
{ nome: 'Geometria algebrica C', anno: 'M', cfu: 6 },
{ nome: 'Geometria algebrica complessa', anno: 'M', cfu: 6 },
{ nome: 'Geometria algebrica D', anno: 'M', cfu: 6 },
{ nome: 'Geometria algebrica E', anno: 'M', cfu: 6 },
{ nome: 'Geometria algebrica F', anno: 'M', cfu: 6 },
{ nome: 'Geometria algebrica G', anno: 'M', cfu: 6 },
{ nome: 'Geometria e analisi complessa', anno: 'M', cfu: 6 },
{ nome: 'Geometria differenziale complessa', anno: 'M', cfu: 6 },
{ nome: 'Geometria iperbolica', anno: 'M', cfu: 6 },
{ nome: 'Geometria riemanniana', anno: 'M', cfu: 6 },
{ nome: 'Gruppi algebrici lineari', anno: 'M', cfu: 6 },
{ nome: 'Gruppi di Coxeter', anno: 'M', cfu: 6 },
{ nome: 'Gruppi di Galois e gruppi fondamentali', anno: 'M', cfu: 6 },
{ nome: 'Meccanica celeste', anno: 'M', cfu: 6 },
{ nome: 'Meccanica spaziale', anno: 'M', cfu: 6 },
{ nome: 'Meccanica superiore', anno: 'M', cfu: 6 },
{ nome: 'Metodi di analisi armonica in analisi non lineare', anno: 'M', cfu: 6 },
{ nome: 'Metodi di approssimazione', anno: 'M', cfu: 6 },
{ nome: 'Metodi matematici della crittografia', anno: 'M', cfu: 6 },
{ nome: 'Metodi matematici della meccanica quantistica', anno: 'M', cfu: 6 },
{ nome: 'Metodi numerici per catene di Markov', anno: 'M', cfu: 6 },
{ nome: 'Metodi numerici per equazioni alle derivate parziali', anno: 'M', cfu: 6 },
{ nome: 'Metodi numerici per il calcolo tensoriale', anno: 'M', cfu: 6 },
{ nome: 'Metodi numerici per il controllo ottimo', anno: 'M', cfu: 6 },
{ nome: 'Metodi numerici per la grafica', anno: 'M', cfu: 6 },
{ nome: 'Metodi numerici per problemi inversi', anno: 'M', cfu: 6 },
{ nome: "Metodi probabilistici per l'algebra lineare numerica", anno: 'M', cfu: 6 },
{ nome: 'Modelli matematici in biomedicina e fisica matematica', anno: 'M', cfu: 6 },
{ nome: 'Origini e sviluppo delle matematiche moderne', anno: 'M', cfu: 6 },
{ nome: 'Probabilità superiore', anno: 'M', cfu: 6 },
{ nome: 'Problemi e metodi della ricerca in didattica della matematica', anno: 'M', cfu: 6 },
{ nome: 'Problemi e metodi in storia della matematica', anno: 'M', cfu: 6 },
{ nome: 'Sistemi dinamici aleatori', anno: 'M', cfu: 6 },
{ nome: 'Statistica superiore', anno: 'M', cfu: 6 },
{ nome: 'Storia della matematica antica e della sua tradizione', anno: 'M', cfu: 6 },
{ nome: 'Superfici di Riemann e curve algebriche', anno: 'M', cfu: 6 },
{ nome: 'Tecnologie per la didattica', anno: 'M', cfu: 6 },
{ nome: 'Teoria algebrica dei numeri 2', anno: 'M', cfu: 6 },
{ nome: 'Teoria analitica dei numeri A', anno: 'M', cfu: 6 },
{ nome: 'Teoria dei giochi', anno: 'M', cfu: 6 },
{ nome: 'Teoria dei modelli', anno: 'M', cfu: 6 },
{ nome: 'Teoria dei nodi A', anno: 'M', cfu: 6 },
{ nome: 'Teoria degli insiemi', anno: 'M', cfu: 6 },
{ nome: 'Teoria degli insiemi A', anno: 'M', cfu: 6 },
{ nome: 'Teoria degli insiemi B', anno: 'M', cfu: 6 },
{ nome: 'Teoria delle categorie', anno: 'M', cfu: 6 },
{ nome: 'Teoria delle rappresentazioni A', anno: 'M', cfu: 6 },
{ nome: "Teoria e metodi dell'ottimizzazione", anno: 'M', cfu: 6 },
{ nome: 'Teoria ergodica', anno: 'M', cfu: 6 },
{ nome: 'Teoria geometrica della misura', anno: 'M', cfu: 6 },
{ nome: 'Topologia algebrica', anno: 'M', cfu: 6 },
{ nome: 'Topologia algebrica A', anno: 'M', cfu: 6 },
{ nome: 'Topologia algebrica B', anno: 'M', cfu: 6 },
{ nome: 'Topologia differenziale', anno: 'M', cfu: 6 },
{ nome: 'Topologia e geometria in bassa dimensione', anno: 'M', cfu: 6 },
{ nome: 'Ultrafiltri e metodi non-standard', anno: 'M', cfu: 6 },
]
export function MediaPesataApp() {
// Funzioni per localStorage
const loadFromStorage = () => {
try {
const savedData = localStorage.getItem('media-pesata-data')
if (savedData) {
const parsed = JSON.parse(savedData)
return {
tipoStudente: parsed.tipoStudente || 'triennale',
corsiSelezionati: parsed.corsiSelezionati || [],
sezioniAperte: parsed.sezioniAperte || {},
mostraRisultati: parsed.mostraRisultati || false,
}
}
} catch (error) {
console.warn('Errore nel caricamento dei dati salvati:', error)
}
return {
tipoStudente: 'triennale' as TipoStudente,
corsiSelezionati: [],
sezioniAperte: {},
mostraRisultati: false,
}
}
const saveToStorage = (data: any) => {
try {
localStorage.setItem('media-pesata-data', JSON.stringify(data))
} catch (error) {
console.warn('Errore nel salvataggio dei dati:', error)
}
}
// Inizializzazione con dati salvati
// const initialData = loadFromStorage()
const [tipoStudente, setTipoStudente] = useState<TipoStudente>('triennale')
const [corsiSelezionati, setCorsiSelezionati] = useState<CorsoSelezionato[]>([])
const [showCustomForm, setShowCustomForm] = useState(false)
const [customCorso, setCustomCorso] = useState<CorsoCustom>({ nome: '', cfu: 0 })
const [sezioniAperte, setSezioniAperte] = useState<Record<string, boolean>>({})
const [mostraRisultati, setMostraRisultati] = useState(false)
// Load data from localStorage on mount
useEffect(() => {
const initialData = loadFromStorage()
setTipoStudente(initialData.tipoStudente)
setCorsiSelezionati(initialData.corsiSelezionati)
setSezioniAperte(initialData.sezioniAperte)
setMostraRisultati(initialData.mostraRisultati)
}, [])
// Salva automaticamente quando cambiano i dati importanti
useEffect(() => {
const dataToSave = {
tipoStudente,
corsiSelezionati,
sezioniAperte,
mostraRisultati,
}
saveToStorage(dataToSave)
}, [tipoStudente, corsiSelezionati, sezioniAperte, mostraRisultati])
const toggleSezione = (nomeSezione: string) => {
setSezioniAperte(prev => ({
...prev,
[nomeSezione]: !prev[nomeSezione],
}))
}
const calcolaMedia = () => {
const corsiConVoto = corsiSelezionati.filter(corso => corso.voto !== null && !corso.passFailOnly)
if (corsiConVoto.length === 0) {
alert('Inserisci almeno un voto per calcolare la media!')
return
}
setMostraRisultati(true)
}
// Funzioni per la gestione dei corsi
const aggiungiCorso = (corso: Corso) => {
// Controlla se la materia esiste già
const materiaEsiste = corsiSelezionati.some(c => c.nome.toLowerCase() === corso.nome.toLowerCase())
if (materiaEsiste) {
alert('Questa materia è già stata aggiunta')
return
}
// Controllo per magistrali: massimo 3 istituzioni
if (tipoStudente === 'magistrale' && corso.anno === 'istituzioni') {
const istituzioniAttuali = corsiSelezionati.filter(c => c.nome.toLowerCase().includes('istituzioni')).length
if (istituzioniAttuali >= 3) {
alert('Puoi selezionare al massimo 3 istituzioni per la magistrale')
return
}
}
const id = Date.now().toString()
const nuovoCorso: CorsoSelezionato = {
id,
nome: corso.nome,
cfu: corso.cfu,
voto: null,
lode: false,
passFailOnly: corso.passFailOnly,
}
setCorsiSelezionati([...corsiSelezionati, nuovoCorso])
}
const aggiungiCorsoCustom = () => {
// Validazioni
if (!customCorso.nome.trim()) {
alert('Il nome della materia non può essere vuoto')
return
}
if (customCorso.cfu <= 0 || customCorso.cfu > 30) {
alert('I CFU devono essere tra 1 e 30')
return
}
if (!Number.isInteger(customCorso.cfu)) {
alert('I CFU devono essere un numero intero')
return
}
// Controlla se la materia esiste già
const materiaEsiste = corsiSelezionati.some(
corso => corso.nome.toLowerCase().trim() === customCorso.nome.toLowerCase().trim(),
)
if (materiaEsiste) {
alert('Questa materia è già stata aggiunta')
return
}
const id = Date.now().toString()
const nuovoCorso: CorsoSelezionato = {
id,
nome: customCorso.nome.trim(),
cfu: customCorso.cfu,
voto: null,
lode: false,
}
setCorsiSelezionati([...corsiSelezionati, nuovoCorso])
setCustomCorso({ nome: '', cfu: 0 })
setShowCustomForm(false)
}
const rimuoviCorso = (id: string) => {
setCorsiSelezionati(corsiSelezionati.filter(corso => corso.id !== id))
}
const aggiornaVoto = (id: string, voto: number | null) => {
// Validazione voto: deve essere tra 18 e 30 e intero
if (voto !== null && (voto < 18 || voto > 30 || !Number.isInteger(voto))) {
return // Ignora valori non validi
}
setCorsiSelezionati(
corsiSelezionati.map(corso =>
corso.id === id ? { ...corso, voto, lode: voto !== 30 ? false : corso.lode } : corso,
),
)
}
const aggiornaLode = (id: string, lode: boolean) => {
setCorsiSelezionati(corsiSelezionati.map(corso => (corso.id === id ? { ...corso, lode } : corso)))
}
const resetTutto = () => {
if (corsiSelezionati.length > 0) {
if (confirm('Sei sicuro di voler cancellare tutte le materie selezionate?')) {
setCorsiSelezionati([])
setCustomCorso({ nome: '', cfu: 0 })
setShowCustomForm(false)
setSezioniAperte({})
setMostraRisultati(false)
// Pulisce anche il localStorage
try {
localStorage.removeItem('media-pesata-data')
} catch (error) {
console.warn('Errore nella pulizia del localStorage:', error)
}
}
}
}
// Calcoli della media pesata
const calcolaMediaPesata = () => {
// Escludi le materie pass/fail dal calcolo
const corsiConVoto = corsiSelezionati.filter(corso => corso.voto !== null && !corso.passFailOnly)
if (corsiConVoto.length === 0) {
return {
mediaPesata: 0,
votoAmmissione: 0,
massimoVotoLaurea: 0,
conLode: false,
bonusLodi: 0,
errore: 'Nessun voto inserito per il calcolo della media',
}
}
// Calcola CFU totali con voto
const cfuTotaliConVoto = corsiConVoto.reduce((sum, corso) => sum + corso.cfu, 0)
const cfuDaEscludere = tipoStudente === 'triennale' ? 15 : 9
// Se i CFU sono insufficienti per l'esclusione, avvisa l'utente
if (cfuTotaliConVoto < cfuDaEscludere) {
return {
mediaPesata: 0,
votoAmmissione: 0,
massimoVotoLaurea: 0,
conLode: false,
bonusLodi: 0,
errore: `Hai inserito solo ${cfuTotaliConVoto} CFU con voto. Servono almeno ${cfuDaEscludere} CFU per applicare le regole di esclusione.`,
}
}
// Ordina per voto crescente
const corsiOrdinati = [...corsiConVoto].sort((a, b) => a.voto! - b.voto!)
let cfuEsclusi = 0
const corsiValidi: CorsoSelezionato[] = []
for (const corso of corsiOrdinati) {
if (cfuEsclusi < cfuDaEscludere) {
const cfuRimanentiDaEscludere = cfuDaEscludere - cfuEsclusi
if (corso.cfu <= cfuRimanentiDaEscludere) {
// Escludi tutto il corso
cfuEsclusi += corso.cfu
} else {
// Escludi solo una parte del corso
const cfuValidi = corso.cfu - cfuRimanentiDaEscludere
corsiValidi.push({ ...corso, cfu: cfuValidi })
cfuEsclusi = cfuDaEscludere
}
} else {
corsiValidi.push(corso)
}
}
// Calcola media pesata
const sommaPesata = corsiValidi.reduce((sum, corso) => sum + corso.voto! * corso.cfu, 0)
const sommaCfu = corsiValidi.reduce((sum, corso) => sum + corso.cfu, 0)
const mediaPesata = sommaCfu > 0 ? sommaPesata / sommaCfu : 0
// Calcola bonus lodi
const bonusLodi = corsiConVoto.reduce((bonus, corso) => {
if (corso.lode) {
return bonus + (corso.cfu > 6 ? 0.5 : 0.25)
}
return bonus
}, 0)
// Cap del bonus lodi basato sul tipo di studente
const capBonusLodi = tipoStudente === 'triennale' ? 1.5 : 2
const bonusLodiFinal = Math.min(bonusLodi, capBonusLodi)
// Voto di ammissione alla laurea (media pesata * 11/3)
const votoAmmissione = (mediaPesata * 11) / 3
// Voto di ammissione finale = voto ammissione + bonus lodi
const votoAmmissioneFinale = votoAmmissione + bonusLodiFinal
// Massimo voto di laurea possibile = voto ammissione + 10 (cappato a 110)
const massimoVotoLaurea = Math.min(votoAmmissioneFinale + 10, 110)
const conLode = massimoVotoLaurea === 110
return {
mediaPesata: Math.round(mediaPesata * 100) / 100,
votoAmmissione: Math.round(votoAmmissioneFinale * 100) / 100,
massimoVotoLaurea: Math.round(massimoVotoLaurea * 100) / 100,
conLode,
bonusLodi: Math.round(bonusLodiFinal * 100) / 100,
errore: null,
}
}
// Filtra corsi disponibili in base al tipo di studente
const getCorsiDisponibili = () => {
if (tipoStudente === 'triennale') {
return CORSI_DISPONIBILI.filter(corso => corso.anno !== 'istituzioni')
} else {
return CORSI_DISPONIBILI.filter(
corso => corso.anno === 'istituzioni' || corso.anno === '3' || corso.anno === 'M',
)
}
}
// Raggruppa corsi per categoria
const raggruppaCorsi = () => {
const corsi = getCorsiDisponibili()
const gruppi: Record<string, Corso[]> = {}
if (tipoStudente === 'triennale') {
gruppi['Primo Anno'] = corsi.filter(c => c.anno === '1')
gruppi['Secondo Anno'] = corsi.filter(c => c.anno === '2')
gruppi['Terzo Anno'] = corsi.filter(c => c.anno === '3')
gruppi['Materie a Scelta'] = corsi.filter(c => c.anno === 'M')
} else {
// Per magistrali: prima le istituzioni, poi tutto il resto come "Materie a Scelta"
gruppi['Istituzioni'] = corsi.filter(c => c.anno === 'istituzioni')
gruppi['Materie a Scelta'] = corsi.filter(c => c.anno === '3' || c.anno === 'M')
}
return gruppi
}
const cambiaTipoStudente = (nuovoTipo: TipoStudente) => {
setTipoStudente(nuovoTipo)
}
const gruppiCorsi = raggruppaCorsi()
const risultati = calcolaMediaPesata()
const totaleCfu = corsiSelezionati.reduce((sum, corso) => sum + corso.cfu, 0)
const maxCfu = tipoStudente === 'triennale' ? 171 : 93 // 180 − 9 e 120 − 27 per la tesi
const cfuError = totaleCfu > maxCfu
return (
<div class="media-pesata-app">
{/* Selezione tipo studente */}
<div class="card student-type-switcher wide">
<div class="grid-center">
<h2>Corso di Laurea</h2>
<div class="compound-button">
<button
class={tipoStudente === 'triennale' ? 'active' : ''}
onClick={() => cambiaTipoStudente('triennale')}
>
Triennale
</button>
<button
class={tipoStudente === 'magistrale' ? 'active' : ''}
onClick={() => cambiaTipoStudente('magistrale')}
>
Magistrale
</button>
</div>
</div>
</div>
{/* Counter CFU */}
<div class={`cfu-counter wide ${cfuError ? 'error' : ''}`}>
<h3>
CFU Totali: {totaleCfu}/{maxCfu}
{tipoStudente === 'triennale' ? ' (+9 tesi)' : ' (+27 tesi)'}
</h3>
{cfuError && <p class="error-text">⚠️ Hai superato il limite di CFU consentiti!</p>}
</div>
{/* Sezione selezione corsi */}
<div class="card">
<div class="title">
<h2>Seleziona Materie</h2>
</div>
{Object.entries(gruppiCorsi).map(([categoria, corsi]) => (
<div key={categoria} class="course-category">
<button onClick={() => toggleSezione(categoria)}>
<div class="h-flex">
{categoria}
<div class="spacer"></div>
<span class={`toggle-icon ${sezioniAperte[categoria] ? 'expanded' : ''}`}>▶</span>
</div>
</button>
{sezioniAperte[categoria] && (
<div class="course-grid">
{corsi.map((corso, index) => (
<button
key={index}
class="course-button"
onClick={() => aggiungiCorso(corso)}
disabled={corsiSelezionati.some(c => c.nome === corso.nome)}
>
<span class="course-name">{corso.nome}</span>
<span class="course-cfu">{corso.cfu} CFU</span>
</button>
))}
</div>
)}
</div>
))}
{/* Form per materia custom */}
<div class="custom-course">
<h3>Materia Personalizzata</h3>
{!showCustomForm ? (
<button onClick={() => setShowCustomForm(true)}>+ Aggiungi Materia Personalizzata</button>
) : (
<div class="custom-form">
<input
type="text"
placeholder="Nome materia"
value={customCorso.nome}
onChange={e =>
setCustomCorso({ ...customCorso, nome: (e.target as HTMLInputElement).value })
}
/>
<input
type="number"
placeholder="CFU"
min="1"
max="30"
step="1"
value={customCorso.cfu || ''}
onChange={e =>
setCustomCorso({
...customCorso,
cfu: parseInt((e.target as HTMLInputElement).value) || 0,
})
}
/>
<button onClick={aggiungiCorsoCustom}>Aggiungi</button>
<button onClick={() => setShowCustomForm(false)}>Annulla</button>
</div>
)}
</div>
</div>
{/* Sezione lista corsi selezionati */}
<div class="card">
<div class="h-flex">
<div class="title">
<h2>Materie Selezionate</h2>
</div>
<div class="spacer"></div>
{corsiSelezionati.length > 0 && <button onClick={resetTutto}>🗑️ Cancella Tutto</button>}
</div>
{corsiSelezionati.length === 0 ? (
<p>Nessuna materia selezionata</p>
) : (
<div class="courses-list">
{corsiSelezionati.map(corso => (
<div key={corso.id} class="course-item">
<span class="course-name">{corso.nome}</span>
<span class="course-cfu">{corso.cfu} CFU</span>
{!corso.passFailOnly && (
<div class="course-grade tall">
<input
type="number"
placeholder="Voto"
min="18"
max="30"
step="1"
value={corso.voto || ''}
onChange={e =>
aggiornaVoto(
corso.id,
parseInt((e.target as HTMLInputElement).value) || null,
)
}
/>
<label class={`lode-checkbox ${corso.voto !== 30 ? 'disabled' : ''}`}>
<input
class="star"
type="checkbox"
checked={corso.lode}
disabled={corso.voto !== 30}
onChange={e =>
aggiornaLode(corso.id, (e.target as HTMLInputElement).checked)
}
/>
Lode
</label>
</div>
)}
<div class="actions tall">
<button
class="icon remove-btn"
onClick={() => rimuoviCorso(corso.id)}
title="Rimuovi materia"
>
×
</button>
</div>
</div>
))}
</div>
)}
</div>
{/* Pulsante Calcola */}
{corsiSelezionati.length > 0 && (
<div class="calculate-section wide">
<button onClick={calcolaMedia}>🧮 Calcola Media e Voto di Laurea</button>
</div>
)}
{/* Risultati */}
{risultati && mostraRisultati && (
<div class="results wide">
<h2>Risultati</h2>
{risultati.errore ? (
<div class="error-message">
<p>⚠️ {risultati.errore}</p>
</div>
) : (
<div class="results-grid">
<div class="result-item">
<span class="label">Media Pesata:</span>
<span class="value">{risultati.mediaPesata}</span>
</div>
<div class="result-item">
<span class="label">Bonus Lodi:</span>
<span class="value">+{risultati.bonusLodi}</span>
</div>
<div class="result-item highlight">
<span class="label">Voto di Ammissione:</span>
<span class="value">{risultati.votoAmmissione}</span>
</div>
<div class="result-item highlight">
<span class="label">Massimo Voto Di Laurea Possibile:</span>
<span class="value">
{risultati.massimoVotoLaurea}
{risultati.conLode && <span class="lode-badge">(+lode)</span>}
</span>
</div>
</div>
)}
</div>
)}
{/* Nota informativa */}
<div class="card wide">
<h3>📋 Come viene calcolata la media</h3>
<div class="info-content">
<h4>Regole di esclusione CFU:</h4>
<ul>
<li>
<strong>Triennale:</strong> I 15 CFU con i voti più bassi vengono esclusi dalla media
</li>
<li>
<strong>Magistrale:</strong> I 9 CFU con i voti più bassi vengono esclusi dalla media
</li>
<li>Se un corso ha più CFU di quelli da escludere, viene diviso proporzionalmente</li>
</ul>
<h4>Calcolo del voto finale:</h4>
<ul>
<li>
<strong>Media pesata:</strong> Somma dei (voto × CFU) diviso per i CFU totali
</li>
<li>
<strong>Bonus lodi:</strong> +0.5 per lodi in materie &gt; 6 CFU, +0.25 per lodi in materie
≤ 6 CFU (max +1.5 per triennale, max +2 per magistrale)
</li>
<li>
<strong>Voto di laurea:</strong> (Voto finale × 11) ÷ 3
</li>
</ul>
<h4>Note:</h4>
<ul>
<li>
Le materie <strong>Pass/Fail</strong> non contribuiscono al calcolo della media
</li>
<li>Il voto finale è limitato a 30</li>
<li>Per i magistrali: massimo 3 istituzioni selezionabili</li>
</ul>
</div>
</div>
</div>
)
}
// Funzione per inizializzare l'app
// export function initMediaPesataApp() {
// const container = document.getElementById('media-pesata-app')
// if (container) {
// render(<MediaPesataApp />, container)
// }
// }
// export default MediaPesataApp
+1
View File
@@ -6,6 +6,7 @@ const links = [
{ href: '/notizie', text: 'Notizie' },
{ href: '/guide', text: 'Guide' },
{ href: '/domande-esami', text: 'Domande Orali' },
{ href: '/media-pesata', text: 'Calcolo Media' }, // Beta testing - solo URL diretto
{ href: '/storia', text: 'Storia' },
// { href: '/login', text: 'Login' },
]
+1 -1
View File
@@ -17,7 +17,7 @@ const { href, imgSrc, style, title } = Astro.props
{imgSrc ? <img src={imgSrc} alt={'logo for ' + title.toLowerCase()} /> : <div class="box" />}
</div>
<div class="title">{title}</div>
<div class="description">
<div class="description text">
<slot />
</div>
</div>
+1 -1
View File
@@ -52,7 +52,7 @@ Le card possono essere di dimensioni diverse. Questa è una card grande.
</div>
```
### Low Level: Mixin SCSS
### ~~Low Level: Mixin SCSS~~ Old CSS Mixin
Non dovrebbe essere mai necessario usarlo direttamente ma l'effetto di ombra delle card è ottenuto con questo mixin SCSS (che si trova in `src/styles/mixins.scss`).
+22
View File
@@ -0,0 +1,22 @@
---
title: Calcola la tua media ed il voto di laurea con il nuovissimo calcolatore del PHC!
description: È ora disponibile uno strumento per calcolare la propria media pesata e il voto di ammissione alla laurea secondo le regole del dipartimento
publishDate: 2025-06-26
---
# Calcola la tua media ed il voto di laurea con il nuovissimo calcolatore del PHC!
È ora disponibile nella sezione "Calcolo Media" del sito uno strumento per calcolare la propria media pesata e il voto di ammissione alla laurea secondo le regole ufficiali del dipartimento.
<p align="center">
<a href="https://phc.dm.unipi.it/media-pesata/">phc.dm.unipi.it/media-pesata/</a>
</p>
Il calcolatore applica automaticamente le regole di esclusione previste dal regolamento:
- **Triennale**: vengono esclusi i 15 CFU con i voti più bassi
- **Magistrale**: vengono esclusi i 9 CFU con i voti più bassi
Il sistema calcola anche il bonus per le lodi, che vale +0.5 punti per ogni materia superiore a 6 CFU e +0.25 punti per materie da 6 CFU o meno, con un tetto massimo di +1.5 punti per la triennale e +2 punti per la magistrale.
Se dovessero esserci bug scriveteci un'email a <a href="mailto:macchinisti@lists.dm.unipi.it">macchinisti@lists.dm.unipi.it</a>!
+104 -1
View File
@@ -19,6 +19,7 @@ names:
analisi-armonica: Analisi Armonica
elementi-di-analisi-complessa: Elementi di Analisi Complessa
eti: Elementi di Teoria degli Insiemi
teoria-dei-nodi: Teoria dei Nodi
groups:
- id: triennale-anno-1
@@ -58,6 +59,7 @@ groups:
- analisi-armonica
- elementi-di-analisi-complessa
- eti
- teoria-dei-nodi
questions:
- course: geometria-2
@@ -2116,7 +2118,7 @@ questions:
- course: geometria-e-topologia-differenziale
content: |
Spazio tangente a $S^n$ (dimostrando che il tangente a $f^{-1}(y) = \ker(df_x)$)
Spazio tangente a $S^n$ (dimostrando che il tangente a $f^{-1}(y) = \ker(df_x)$)
tags:
- 2023
@@ -2236,3 +2238,104 @@ questions:
e illustrare la formula di Gauss
tags:
- 2023
- course: teoria-dei-nodi
content: |
Definizione di superficie di Seifert e sue proprietà locali.
tags:
- 2025
- course: teoria-dei-nodi
content: |
Teorema di Seifert, enunciato e dimostrazione.
tags:
- 2025
- course: teoria-dei-nodi
content: |
Definizione del genere di un nodo (tramite superficie di Seifert).
tags:
- 2025
- course: teoria-dei-nodi
content: |
Proprietà del genere di un nodo: genere del nodo banale ($g(U)=0$), additività rispetto alla somma connessa ($g(K_1 \# K_2) = g(K_1) + g(K_2)$).
tags:
- 2025
- course: teoria-dei-nodi
content: |
Relazione tra caratteristica di Eulero di una superficie di Seifert e il genere del nodo.
tags:
- 2025
- course: teoria-dei-nodi
content: |
Genere del nodo immagine speculare $m(K)$.
tags:
- 2025
- course: teoria-dei-nodi
content: |
Minimalità del genere di un nodo (genere di una superficie di Seifert minima).
tags:
- 2025
- course: teoria-dei-nodi
content: |
Struttura del complementare di una superficie di Seifert $S$ in $\mathbb{R}^3$.
tags:
- 2025
- course: teoria-dei-nodi
content: |
Numero di allacciamento
tags:
- 2025
- course: teoria-dei-nodi
content: |
Verifica che $\text{lk}$ è un invariante
tags:
- 2025
- course: teoria-dei-nodi
content: |
Interpretazione omologica di $\text{lk}$.
tags:
- 2025
- course: teoria-dei-nodi
content: |
Classi di omotopia vs isotopia
tags:
- 2025
- course: teoria-dei-nodi
content: |
Due curve semplici chiuse sul toro sono isotope $\Leftrightarrow$ solo la stessa classe a meno del segno in $H_1(S^1 \times S^1)$
tags:
- 2025
- course: teoria-dei-nodi
content: |
$L = \alpha \cup \beta$, $\text{lk}(L) = \#(\alpha \cap G) = \#(\beta \cap F)$
tags:
- 2025
- course: teoria-dei-nodi
content: |
Forma di Seifert
tags:
- 2025
- course: teoria-dei-nodi
content: |
Modulo di Alexander
tags:
- 2025
- course: teoria-dei-nodi
content: |
Struttura di $H_1(X_\infty)$
tags:
- 2025
- course: teoria-dei-nodi
content: |
$H_1(X_\infty)$ è presentato da $tA - A^T$.
tags:
- 2025
- course: teoria-dei-nodi
content: |
Differenza tra superfici di Seifert: S-equivalenza e invarianza per mosse di Reidemeister.
tags:
- 2025
- course: teoria-dei-nodi
content: |
Relazioni tra le matrici di Seifert.
tags:
- 2025
+8 -6
View File
@@ -9,20 +9,17 @@ import '@fontsource/iosevka/latin.css'
import '@fontsource-variable/material-symbols-outlined/full.css'
import '../styles/main.scss'
import '@/styles/main.css'
type Props = {
title?: string
description?: string
thumbnail?: string
/** Tags for the page, used for styling */
pageTags?: string | string[]
}
import phcIcon from '../assets/icon.png'
const { title, description, thumbnail, pageTags } = Astro.props
const { title, description, thumbnail } = Astro.props
---
<!doctype html>
@@ -75,9 +72,14 @@ const { title, description, thumbnail, pageTags } = Astro.props
src="//analytics.phc.dm.unipi.it/count.js"
data-goatcounter="https://analytics.phc.dm.unipi.it/count"></script>
<style is:inline>
/* Workaround Astro CSS loading order, this forces the layering upfront */
@layer base, typography, component, page, utility;
</style>
<title>{title ?? 'PHC'}</title>
</head>
<body class:list={typeof pageTags === 'string' ? [pageTags] : pageTags}>
<body>
<slot />
</body>
</html>
+2
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/appunti.css'
import PageLayout from '@layouts/PageLayout.astro'
import { AppuntiList, AppuntiCard } from '@client/Appunti'
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/domande-esami.css'
import type { GetStaticPaths } from 'astro'
import BaseLayout from '@/layouts/BaseLayout.astro'
import Footer from '@/components/Footer.astro'
@@ -16,7 +18,7 @@ export const getStaticPaths = (() => {
const { course } = Astro.params
---
<BaseLayout title="Domande Orali | PHC" pageTags={'domande-esami'}>
<BaseLayout title="Domande Orali | PHC">
<Header />
<main>
<DomandeEsamiCourse client:only="preact" course={course} />
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/domande-esami.css'
import { PhosphorIcon } from '@/client/Icon'
import Footer from '@/components/Footer.astro'
import Header from '@/components/Header.astro'
@@ -14,7 +16,7 @@ const courseQuestionCounts = Object.fromEntries(
)
---
<BaseLayout title="Domande Orali | PHC" pageTags={'domande-esami'}>
<BaseLayout title="Domande Orali | PHC">
<Header />
<main>
<h1>Domande Orali</h1>
+5 -5
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/guide-item.css'
import { getCollection } from 'astro:content'
import ArticleLayout from '@/layouts/ArticleLayout.astro'
@@ -16,11 +18,9 @@ const { entry } = Astro.props
const { Content } = await entry.render()
---
<ArticleLayout
{...entry.data}
title={entry.data.title + ' | Guide | PHC'}
pageTags={['guida', entry.data.id, entry.data.series && 'series']}
>
<ArticleLayout {...entry.data} title={entry.data.title + ' | Guide | PHC'}>
<!-- pageTags={['guida', entry.data.id, entry.data.series && 'series']} -->
<h1>{entry.data.title}</h1>
{entry.data.series && <div class="series">Serie: {entry.data.series}</div>}
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/guide-list.css'
import { getCollection } from 'astro:content'
import PageLayout from '@layouts/PageLayout.astro'
@@ -6,7 +8,7 @@ import PageLayout from '@layouts/PageLayout.astro'
const guides = await getCollection('guides')
---
<PageLayout title="Guide | PHC" pageTags="guide">
<PageLayout title="Guide | PHC">
<h1>
<a href="/guide">Guide</a>
</h1>
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/guide-list.css'
import { getCollection } from 'astro:content'
import type { CollectionEntry } from 'astro:content'
import PageLayout from '@/layouts/PageLayout.astro'
@@ -33,7 +35,7 @@ interface Props {
const { tag, guides } = Astro.props
---
<PageLayout title={`#${tag} | Guide | PHC`} pageTags="guide tag">
<PageLayout title={`#${tag} | Guide | PHC`}>
<h1><a href="/guide">Guide</a> > <a href={`/guide/tags/${tag}`}>#{tag}</a></h1>
<div class="card-list">
{
+2
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/homepage.css'
import { getCollection } from 'astro:content'
import PageLayout from '@/layouts/PageLayout.astro'
import { Content as WhatPhcContent, frontmatter as whatsPhcFrontmatter } from '@/content/meta/whats-phc.md'
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/macchinisti.css'
import BaseLayout from '../layouts/BaseLayout.astro'
import Header from '../components/Header.astro'
import Footer from '../components/Footer.astro'
@@ -26,7 +28,7 @@ const getMacchinistaPicture = (fullName: string) => {
}
---
<BaseLayout title="Macchinisti | PHC" pageTags={'macchinisti'}>
<BaseLayout title="Macchinisti | PHC">
<Header />
<main>
<div class="card large" style={{ '--card-base': '#e1766b' }}>
+21
View File
@@ -0,0 +1,21 @@
---
import '@/styles/pages/media-pesata.css'
import PageLayout from '../layouts/PageLayout.astro'
import { MediaPesataApp } from '@/client/MediaPesataApp'
---
<PageLayout
title="Voto Laurea"
description="Calcola la tua media pesata e il voto di laurea seguendo le regole del dipartimento"
>
<div class="media-pesata-container">
<h1>Calcolo Media e Voto di Laurea</h1>
<p>
Calcola la tua media pesata e il voto con cui ti siederai alla discussione di laurea, seguendo le regole del
dipartimento di Matematica.
</p>
<MediaPesataApp client:load />
</div>
</PageLayout>
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/meta-design.css'
import BaseLayout from '../../layouts/BaseLayout.astro'
import Header from '../../components/Header.astro'
@@ -11,7 +13,7 @@ import { Content, getHeadings } from '../../content/meta/design.mdx'
const headings = getHeadings()
---
<BaseLayout {...Astro.props} pageTags="design">
<BaseLayout {...Astro.props}>
<Header />
<aside>
<nav>
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/news-item.css'
import { getCollection } from 'astro:content'
import ArticleLayout from '../../layouts/ArticleLayout.astro'
@@ -15,6 +17,6 @@ const { entry } = Astro.props
const { Content } = await entry.render()
---
<ArticleLayout {...entry.data} title={entry.data.title + ' | Notizie | PHC'} pageTags={['notizia']}>
<ArticleLayout {...entry.data} title={entry.data.title + ' | Notizie | PHC'}>
<Content />
</ArticleLayout>
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/news-list.css'
import { getCollection } from 'astro:content'
import PageLayout from '@layouts/PageLayout.astro'
@@ -6,7 +8,7 @@ import PageLayout from '@layouts/PageLayout.astro'
const news = await getCollection('news')
---
<PageLayout title="Notizie | PHC" pageTags="notizie">
<PageLayout title="Notizie | PHC">
<h1><a href="/notizie">Notizie</a></h1>
<div class="card-list">
{
+3 -1
View File
@@ -1,4 +1,6 @@
---
import '@/styles/pages/storia.css'
import BaseLayout from '../layouts/BaseLayout.astro'
import Header from '../components/Header.astro'
@@ -10,7 +12,7 @@ import imgCluster from '@/assets/gallery/001-cluster-fra-luca@4x3.jpg'
import WebSite from '@/assets/gallery/005-website-development@3x4.jpg'
---
<BaseLayout title="Storia | PHC" pageTags={'storia'}>
<BaseLayout title="Storia | PHC">
<Header />
<main>
<div class="card large" style={{ '--card-base': '#ffd3a0' }}>
+3 -1
View File
@@ -1,10 +1,12 @@
---
import '@/styles/pages/utenti.css'
import PageLayout from '../layouts/PageLayout.astro'
import { UtentiPage } from '../client/UtentiPage.tsx'
---
<PageLayout title="Utenti | PHC" pageTags="utenti">
<PageLayout title="Utenti | PHC">
<h1>Utenti</h1>
<UtentiPage client:load />
</PageLayout>
@@ -1,4 +1,6 @@
@mixin neo-brutalist-card($size: 3px, $offset: $size + 1, $shadow: true, $hoverable: false) {
/* This file is here for historical reasons but is not used anymore */
/* @mixin neo-brutalist-card($size: 3px, $offset: $size + 1, $shadow: true, $hoverable: false) {
border: $size solid #222;
border-radius: $size * 2;
@@ -14,4 +16,4 @@
box-shadow: $offset + 1 $offset + 1 0 0 #222;
}
}
}
} */
+27
View File
@@ -0,0 +1,27 @@
/* This file is here for historical reasons but is not used anymore */
@layer page {
/*
.login {
background: #ddfaff;
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
max-width: 80ch;
padding: 3rem 0;
gap: 3rem;
h3 {
font-size: 28px;
font-weight: 600;
}
}
}
*/
}
@@ -1,12 +1,15 @@
// $news-bg: #fffbeb;
// $news-accent-bg: #f8e8b1;
/*
$news-bg: #fffbeb;
$news-accent-bg: #f8e8b1;
*/
@import './mixins.scss';
/* @TODO: SCSS conversion - @import becomes more complex */
/* @import './mixins.scss'; */
@layer component {
//
// Components - for complex parts of the UI like search bars or compound buttons
//
/*
/* Components - for complex parts of the UI like search bars or compound buttons
*/
.phosphor-icon {
box-sizing: content-box;
@@ -52,7 +55,13 @@
width: 100%;
height: 2.5rem;
@include neo-brutalist-card;
/* @TODO: SCSS conversion - mixin */
/* @include neo-brutalist-card; */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 4px 4px 0 0 #222;
background: #fff;
display: grid;
grid-template-columns: 1fr auto;
@@ -60,8 +69,6 @@
cursor: pointer;
background: #fff;
&:hover,
&:hover input[type='text'] {
background: #f8f8f8;
@@ -105,7 +112,11 @@
background: #fff;
@include neo-brutalist-card;
/* @TODO: SCSS conversion - mixin */
/* @include neo-brutalist-card; */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 4px 4px 0 0 #222;
a {
display: contents;
@@ -161,7 +172,16 @@
aspect-ratio: 10 / 14;
background: #d0d0d0;
@include neo-brutalist-card($hoverable: true);
/* @TODO: SCSS conversion - mixin */
/* @include neo-brutalist-card($hoverable: true); */
border: 3px solid #222;
border-radius: 6px;
transition: all 64ms linear;
&:hover {
transform: translate(-1px, -1px);
box-shadow: 4px 4px 0 0 #222;
}
}
& > .thumbnail + * {
@@ -195,7 +215,11 @@
background: var(--card-bg, var(--project-card-bg));
color: #000e;
@include neo-brutalist-card($size: 3px, $offset: 9px);
/* @TODO: SCSS conversion - mixin */
/* @include neo-brutalist-card($size: 3px, $offset: 9px); */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 9px 9px 0 0 #222;
row-gap: 0.5rem;
padding: 1rem;
@@ -219,109 +243,109 @@
display: grid;
grid-auto-flow: column;
// .news-item {
// background: $news-bg;
// color: #111;
/* .news-item {
background: $news-bg;
color: #111;
// @include neo-brutalist-card($size: 3px, $offset: 9px);
@include neo-brutalist-card($size: 3px, $offset: 9px);
// display: flex;
// flex-direction: column;
display: flex;
flex-direction: column;
// width: 22rem;
// max-height: 27rem;
width: 22rem;
max-height: 27rem;
// overflow: hidden;
overflow: hidden;
// ::-webkit-scrollbar {
// width: 10px;
// }
::-webkit-scrollbar {
width: 10px;
}
// ::-webkit-scrollbar-thumb {
// background-color: #c67e14;
// border: 2px solid #222;
::-webkit-scrollbar-thumb {
background-color: #c67e14;
border: 2px solid #222;
// &:hover {
// background-color: #e69419;
// }
// }
&:hover {
background-color: #e69419;
}
}
// a {
// font-weight: 600;
// text-decoration: none;
// color: #c67e14;
a {
font-weight: 600;
text-decoration: none;
color: #c67e14;
// &:hover {
// text-decoration: underline solid 2px;
// }
// }
&:hover {
text-decoration: underline solid 2px;
}
}
// & > .title {
// padding: 1rem;
// background: $news-accent-bg;
// line-height: 1;
// font-size: 26px;
// }
& > .title {
padding: 1rem;
background: $news-accent-bg;
line-height: 1;
font-size: 26px;
}
// a.title {
// color: #83530c;
// }
a.title {
color: #83530c;
}
// & > .abstract {
// flex-grow: 1;
& > .abstract {
flex-grow: 1;
// padding: 1rem;
padding: 1rem;
// overflow-y: auto;
overflow-y: auto;
// @extend .text;
// }
@extend .text;
}
// & > .content {
// display: flex;
// padding: 1rem;
// flex-direction: column;
// gap: 0.5rem;
& > .content {
display: flex;
padding: 1rem;
flex-direction: column;
gap: 0.5rem;
// background: #fff8da;
background: #fff8da;
// & > .continue {
// padding: 1rem;
& > .continue {
padding: 1rem;
// display: grid;
// align-items: end;
// justify-content: end;
// }
display: grid;
align-items: end;
justify-content: end;
}
// & > .description {
// font-size: 16px;
// line-height: 1.5;
& > .description {
font-size: 16px;
line-height: 1.5;
// flex-grow: 1;
// }
flex-grow: 1;
}
// & > .tags {
// display: flex;
// gap: 0.5rem;
// flex-wrap: wrap;
// font-size: 14px;
// color: #555;
// }
& > .tags {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
font-size: 14px;
color: #555;
}
// & > .date {
// font-size: 14px;
// font-style: italic;
// font-weight: 600;
// color: #0008;
// }
& > .date {
font-size: 14px;
font-style: italic;
font-weight: 600;
color: #0008;
}
// & > .author {
// font-weight: 600;
// font-size: 15px;
// color: #555;
// }
// }
// }
& > .author {
font-weight: 600;
font-size: 15px;
color: #555;
}
}
} */
}
.timeline {
@@ -334,7 +358,7 @@
max-width: 120ch;
grid-template-columns: 1fr var(--timeline-track-size) 1fr;
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
--timeline-track-size: 3rem;
grid-template-columns: var(--timeline-track-size) 1fr;
}
@@ -348,7 +372,7 @@
grid-template-columns: auto;
padding: 2rem 1rem;
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
padding: 1rem 1rem 1rem 0;
}
@@ -364,7 +388,7 @@
}
}
// timeline vertical line
/* timeline vertical line */
&::before {
content: '';
@@ -386,7 +410,7 @@
bottom: 50%;
}
// timeline circle
/* timeline circle */
&::after {
content: '';
@@ -406,7 +430,7 @@
grid-column: 1 / span 1;
}
@media screen and (min-width: $screen-desktop-min) {
@media screen and (min-width: 1024px) {
&:nth-child(odd)::before {
grid-column: 2 / span 1;
}
@@ -425,7 +449,11 @@
}
.dropdown {
@include neo-brutalist-card;
/* @TODO: SCSS conversion - mixin */
/* @include neo-brutalist-card; */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 3px 3px 0 0 #222;
max-width: 15rem;
position: absolute;
@@ -435,8 +463,12 @@
border-top: none;
border-radius: 0 0 0.5rem 0.5rem;
@media screen and (max-width: $screen-desktop-min) {
@include neo-brutalist-card;
@media screen and (max-width: 1024px) {
/* @TODO: SCSS conversion - mixin */
/* @include neo-brutalist-card; */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 3px 3px 0 0 #222;
position: static;
transform: none;
@@ -444,7 +476,7 @@
margin-right: auto;
}
}
@media screen and (min-width: $screen-desktop-min) {
@media screen and (min-width: 1024px) {
&:nth-child(odd) {
grid-column: 1 / span 2;
grid-template-columns: 1fr var(--timeline-track-size);
@@ -475,7 +507,7 @@
}
}
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
grid-column: 1 / span 2;
grid-template-columns: var(--timeline-track-size) 1fr;
@@ -487,9 +519,9 @@
}
}
//
// Cards
//
/*
/* Cards
*/
.card {
display: grid;
@@ -499,17 +531,25 @@
background: var(--card-base-internal);
color: color-mix(in srgb, var(--card-base-internal), #000 80%);
@include neo-brutalist-card($size: 3px, $offset: 9px);
/* @TODO: SCSS conversion - mixin */
/* @include neo-brutalist-card($size: 3px, $offset: 9px); */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 9px 9px 0 0 #222;
row-gap: 0.5rem;
padding: 1rem;
// Variants
/* Variants */
&.large {
padding: 2rem;
@include neo-brutalist-card($size: 4px, $offset: 8px);
/* @TODO: SCSS conversion - mixin */
/* @include neo-brutalist-card($size: 4px, $offset: 8px); */
border: 4px solid #222;
border-radius: 8px;
box-shadow: 8px 8px 0 0 #222;
row-gap: 1rem;
@@ -523,7 +563,7 @@
}
}
// Child Items
/* Child Items */
& > .title {
color: color-mix(in srgb, var(--card-base-internal), #000 75%);
@@ -580,7 +620,7 @@
gap: 0.5rem;
}
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
padding: 0.9rem;
&.large {
@@ -629,9 +669,9 @@
display: contents;
}
//
// Card List
//
/*
/* Card List
*/
.card-list {
display: grid;
@@ -663,7 +703,7 @@
gap: 1rem;
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
grid-template-columns: 1fr;
& > .search {
@@ -673,44 +713,44 @@
}
/* .filter-select {
width: 100%;
height: 2.5rem;
width: 100%;
height: 2.5rem;
@include neo-brutalist-card;
@include neo-brutalist-card;
display: grid;
align-items: center;
grid-auto-flow: column;
display: grid;
align-items: center;
grid-auto-flow: column;
cursor: pointer;
cursor: pointer;
--filter-bg-color-hover: color-mix(in srgb, var(--filter-bg-color, #ddd), #000 10%);
--filter-bg-color-hover: color-mix(in srgb, var(--filter-bg-color, #ddd), #000 10%);
background: var(--filter-bg-color, #ddd);
background: var(--filter-bg-color, #ddd);
&:hover,
&:hover select {
background: var(--filter-bg-color-hover);
}
&:hover,
&:hover select {
background: var(--filter-bg-color-hover);
}
.material-symbols-outlined {
padding: 0 0.35rem;
}
.material-symbols-outlined {
padding: 0 0.35rem;
}
select {
border: none;
box-shadow: none;
outline: none;
height: 100%;
appearance: none;
select {
border: none;
box-shadow: none;
outline: none;
height: 100%;
appearance: none;
cursor: pointer;
cursor: pointer;
padding: 0;
padding: 0;
background: var(--filter-bg-color, #ddd);
}
} */
background: var(--filter-bg-color, #ddd);
}
} */
.combobox {
width: 100%;
@@ -718,7 +758,10 @@
position: relative;
padding: 0 0.25rem 0 0.25rem;
@include neo-brutalist-card;
/* @TODO: SCSS conversion - mixin */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 4px 4px 0 0 #222;
cursor: pointer;
@@ -753,7 +796,10 @@
top: calc(100% + 8px);
left: -3px;
@include neo-brutalist-card;
/* @TODO: SCSS conversion - mixin */
border: 3px solid #222;
border-radius: 6px;
/* box-shadow: 3px 3px 0 0 #222; */
background: #fff;
@@ -770,7 +816,7 @@
}
}
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
.dropdown {
left: 3px;
right: 3px;
@@ -781,10 +827,10 @@
}
.gallery-collage {
// display: flex;
// flex-wrap: wrap;
/* display: flex;
flex-wrap: wrap;
// width: 64rem;
width: 64rem; */
width: 100%;
max-width: 100%;
@@ -799,9 +845,9 @@
gap: 1rem;
place-content: center;
// align-items: center;
/* align-items: center; */
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
grid-template-columns: auto;
grid-template-rows: auto;
grid-auto-rows: auto;
@@ -818,7 +864,7 @@
grid-column: span var(--cols, 1);
grid-row: span var(--rows, 1);
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
max-width: 100%;
grid-column: span 1;
@@ -837,7 +883,7 @@
object-fit: cover;
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
max-height: none;
width: 25rem;
@@ -878,7 +924,7 @@
font-size: 15px;
font-weight: 700;
// gold badge
/* gold badge */
background: #ffdb12;
color: #725306;
@@ -928,7 +974,7 @@
width: 100%;
justify-content: center;
// align-items: start;
/* align-items: start; */
.text > * {
max-width: none;
@@ -939,7 +985,7 @@
grid-template-rows: 1fr auto;
}
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
grid-template-columns: 1fr;
padding: 0;
@@ -1000,7 +1046,7 @@
cursor: pointer;
}
// just to know for reference
/* just to know for reference */
.fake-masonry {
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
@@ -1,10 +1,10 @@
@import './mixins.scss';
/* @import './mixins.scss'; */
//
// Controls - for things like buttons, input, select
//
/*
Controls - for things like buttons, input, select
*/
@layer common {
@layer base {
button,
.button,
[role='button'] {
@@ -12,7 +12,10 @@
background: #fff;
@include neo-brutalist-card;
/* @include neo-brutalist-card; */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 4px 4px 0 0 #222;
transition: all 64ms linear;
@@ -35,7 +38,7 @@
text-decoration: none;
color: #222;
font-family: var(--font-secondary);
font-family: var(--font-secondary); /* TODO: check if this is a global variable and replace */
font-weight: 600;
cursor: pointer;
@@ -72,9 +75,12 @@
input[type='text'],
input[type='password'] {
width: 100%;
height: 2.5rem;
min-height: 1.75rem;
@include neo-brutalist-card;
/* @include neo-brutalist-card; */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 4px 4px 0 0 #222;
padding: 0 0.25rem;
@@ -83,6 +89,95 @@
}
}
input[type='checkbox'] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: calc(1.5rem + 1px);
height: calc(1.5rem + 1px);
background: #fff;
border: 3px solid #222;
border-radius: 4px;
box-shadow: 3px 3px 0 0 #222;
position: relative;
cursor: pointer;
transition: all 64ms linear;
&:hover {
transform: translate(-1px, -1px);
box-shadow: 4px 4px 0 0 #222;
}
&:active {
transform: translate(1px, 1px);
box-shadow: 2px 2px 0 0 #222;
}
&:checked {
background: #1e6733;
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 0.8rem;
height: 0.8rem;
background: #1e6733;
clip-path: polygon(10% 55%, 35% 75%, 85% 25%, 90% 35%, 40% 95%, 5% 60%);
}
&:hover {
background: #2b8b47;
}
}
&:disabled {
background: #eee;
border-color: #888;
box-shadow: 3px 3px 0 0 #888;
cursor: not-allowed;
&:hover {
transform: none;
box-shadow: 3px 3px 0 0 #888;
}
&:checked {
background: #aaa;
&::after {
background: #666;
}
}
}
&.star {
&:checked::after {
background: rgb(255, 197, 49);
clip-path: polygon(
50% 0%,
61% 35%,
98% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
2% 35%,
39% 35%
);
}
}
}
form {
display: grid;
gap: 1rem;
@@ -92,7 +187,10 @@
min-width: 40ch;
@include neo-brutalist-card($size: 3px, $offset: 9px);
/* @include neo-brutalist-card($size: 3px, $offset: 9px); */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 9px 9px 0 0 #222;
button,
.button,
@@ -165,7 +263,8 @@
}
}
@media screen and (max-width: $screen-desktop-min) {
@media screen and (max-width: 1024px) {
/* TODO: check if this is a global variable and replace */
summary {
place-content: stretch;
place-items: stretch;
+371
View File
@@ -0,0 +1,371 @@
@layer base, typography, component, page, utility;
@import url(./controls.css);
@import url(./components.css);
@import url(./typography.css);
/* $screen-desktop-min: 1024px; */
/* @TODO: SCSS conversion - @import becomes more complex */
/* @import './mixins.scss'; */
/* @import './typography.scss'; */
:root {
--palette-black: #222;
--border-large: 4px solid var(--palette-black);
--header-bg: #fff;
--footer-bg: #444;
--footer-fg: #fdfdfd;
--homepage-principal-bg: #ecffe3;
--homepage-whatsphc-bg: #e4c5ff;
--homepage-news-bg: #c2a8eb;
--homepage-projects-bg: #f5f2cc;
--homepage-macchinisti-bg: #888;
--project-card-bg: #a2d4f3;
--font-primary: 'Open Sans', sans-serif;
--font-display: 'Iosevka', monospace;
--font-mono: 'Source Code Pro', monospace;
--font-secondary: 'Source Sans Pro', sans-serif;
}
:root {
--guide-base: #a2d4f3;
--guide-darkest: color-mix(in srgb, var(--guide-base), #000 75%);
--guide-darker: color-mix(in srgb, var(--guide-base), #000 50%);
--guide-dark: color-mix(in srgb, var(--guide-base), #000 25%);
--guide-light: color-mix(in srgb, var(--guide-base), #fff 25%);
--guide-lighter: color-mix(in srgb, var(--guide-base), #fff 50%);
--guide-lightest: color-mix(in srgb, var(--guide-base), #fff 75%);
--news-base: #f8e8b1;
}
@layer base {
*,
*::before,
*::after {
box-sizing: border-box;
font: inherit;
margin: 0;
}
html {
height: 100%;
}
html,
body {
min-height: 100%;
margin: 0;
font-family: var(--font-primary);
font-size: 18px;
color: #222;
}
/*
html {
scroll-snap-type: y mandatory;
scroll-padding-top: 4rem;
}
*/
img {
display: block;
}
a {
color: inherit;
text-decoration: none;
}
body {
/*
for the header spacing
padding-top: 6rem;
*/
display: grid;
grid-template-rows: auto 1fr auto;
header {
z-index: 10;
height: 6rem;
border-bottom: var(--border-large);
background: var(--header-bg);
grid-column: 1 / -1;
top: 0;
position: sticky;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.logo {
padding-left: 1rem;
img {
height: 3.5rem;
}
}
.links {
display: flex;
align-items: center;
gap: 1.5rem;
padding: 0 1.5rem;
a {
font-family: var(--font-display);
font-size: 18px;
font-weight: 500;
letter-spacing: 1px;
color: #333;
padding: 0.25rem 1.325rem;
}
@media screen and (max-width: 1024px) {
flex-direction: column;
}
}
.side-menu {
position: fixed;
/*
top: 0;
right: 0;
bottom: 0;
left: 3rem;
*/
top: 5rem;
right: 0;
bottom: 0;
left: 0;
background: #f0f0f0;
display: grid;
/*
grid-template-rows: auto 1fr;
*/
grid-template-rows: 1fr;
gap: 1.5rem;
padding: 1.5rem;
justify-content: center;
/*
& > :first-child {
justify-self: end;
}
*/
.links {
display: grid;
grid-template-columns: 1fr;
align-content: start;
width: calc(100vw - 3rem);
max-width: 100%;
gap: 1.5rem;
padding: 0;
}
}
#header-menu-toggle {
display: none;
&:not(:checked) ~ .side-menu {
display: none;
}
}
&:has(#header-menu-toggle:checked) #header-menu-toggle-menu {
display: none;
}
&:has(#header-menu-toggle:not(:checked)) #header-menu-toggle-close {
display: none;
}
@media screen and (max-width: 1024px) {
height: 5rem;
padding: 0 0.5rem;
.logo {
padding-left: 0;
img {
height: 3rem;
}
}
}
}
main {
width: 100%;
@media screen and (max-width: 1024px) {
padding: 3rem 1rem;
gap: 3rem;
.card {
width: 100%;
}
}
}
footer {
z-index: 10;
padding: 1rem 0;
--paragraph-margin: 0.5rem;
--zone-color: #aaa;
min-height: 6rem;
border-top: var(--border-large);
background: var(--footer-bg);
color: var(--footer-fg);
display: grid;
place-content: center;
font-family: var(--font-secondary);
font-size: 18px;
scroll-snap-align: end;
@media screen and (max-width: 1024px) {
min-height: 5rem;
font-size: 18px;
}
}
}
::-webkit-scrollbar-track:vertical {
background-color: #f0f0f0;
border-left: 2px solid #222;
border-top: 2px solid #222;
border-bottom: 2px solid #222;
}
::-webkit-scrollbar-track:horizontal {
background-color: #f0f0f0;
border-top: 2px solid #222;
border-left: 2px solid #222;
border-right: 2px solid #222;
}
::-webkit-scrollbar-thumb {
background-color: #1e6733;
border: 2px solid #222;
}
::-webkit-scrollbar-thumb:hover {
background-color: #2b8b47;
}
::-webkit-scrollbar-corner {
background-color: #f0f0f0;
/* border-left: 2px solid #222; */
/* border-top: 2px solid #222; */
}
::-webkit-scrollbar {
width: 15px;
}
::selection {
background: #0002;
}
body:has(#header-menu-toggle:checked) {
overflow: hidden;
}
}
/*
Typography
*/
/* @TODO: SCSS conversion - @import becomes more complex */
/* @import './controls.scss'; */
/* @import './components.scss'; */
/*
Custom Page Styles
*/
/* @TODO: SCSS conversion - @import becomes more complex */
/* @import './pages.scss'; */
@layer utility {
.hide {
display: none !important;
}
.invisible {
opacity: 0 !important;
}
.grid-center {
display: grid;
place-content: center;
place-items: center;
}
.h-flex {
display: flex;
gap: 0.5rem;
flex-direction: row;
}
.v-flex {
display: flex;
gap: 0.5rem;
flex-direction: column;
}
.h-flex,
.v-flex {
place-self: stretch;
align-items: center;
> * {
flex-shrink: 0;
}
> .spacer {
flex-grow: 1;
}
}
@media screen and (min-width: 1024px) {
.mobile-only {
display: none !important;
}
}
@media screen and (max-width: 1024px) {
.desktop-only {
display: none !important;
}
}
}
-315
View File
@@ -1,315 +0,0 @@
$screen-desktop-min: 1024px;
:root {
--palette-black: #222;
--border-large: 4px solid var(--palette-black);
--header-bg: #fff;
--footer-bg: #444;
--footer-fg: #fdfdfd;
--homepage-principal-bg: #ecffe3;
--homepage-whatsphc-bg: #e4c5ff;
--homepage-news-bg: #c2a8eb;
--homepage-projects-bg: #f5f2cc;
--homepage-macchinisti-bg: #888;
--project-card-bg: #a2d4f3;
--font-primary: 'Open Sans', sans-serif;
--font-display: 'Iosevka', monospace;
--font-mono: 'Source Code Pro', monospace;
--font-secondary: 'Source Sans Pro', sans-serif;
}
@layer common, typography, component, page;
@import './mixins.scss';
@import './typography.scss';
@layer common {
*,
*::before,
*::after {
box-sizing: border-box;
font: inherit;
margin: 0;
}
html {
height: 100%;
}
html,
body {
min-height: 100%;
margin: 0;
font-family: var(--font-primary);
font-size: 18px;
color: #222;
}
// html {
// scroll-snap-type: y mandatory;
// scroll-padding-top: 4rem;
// }
img {
display: block;
}
a {
color: inherit;
text-decoration: none;
}
}
//
// Typography
//
@import './controls.scss';
@import './components.scss';
//
// Custom Page Styles
//
body {
// for the header spacing
// padding-top: 6rem;
display: grid;
grid-template-rows: auto 1fr auto;
header {
z-index: 10;
height: 6rem;
border-bottom: var(--border-large);
background: var(--header-bg);
grid-column: 1 / -1;
top: 0;
position: sticky;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.logo {
padding-left: 1rem;
img {
height: 3.5rem;
}
}
.links {
display: flex;
align-items: center;
gap: 1.5rem;
padding: 0 1.5rem;
a {
font-family: var(--font-display);
font-size: 18px;
font-weight: 500;
letter-spacing: 1px;
color: #333;
padding: 0.25rem 1.325rem;
}
@media screen and (max-width: $screen-desktop-min) {
flex-direction: column;
}
}
.side-menu {
position: fixed;
// top: 0;
// right: 0;
// bottom: 0;
// left: 3rem;
top: 5rem;
right: 0;
bottom: 0;
left: 0;
background: #f0f0f0;
display: grid;
// grid-template-rows: auto 1fr;
grid-template-rows: 1fr;
gap: 1.5rem;
padding: 1.5rem;
justify-content: center;
// & > :first-child {
// justify-self: end;
// }
.links {
display: grid;
grid-template-columns: 1fr;
align-content: start;
width: calc(100vw - 3rem);
max-width: 100%;
gap: 1.5rem;
padding: 0;
}
}
#header-menu-toggle {
display: none;
&:not(:checked) ~ .side-menu {
display: none;
}
}
&:has(#header-menu-toggle:checked) #header-menu-toggle-menu {
display: none;
}
&:has(#header-menu-toggle:not(:checked)) #header-menu-toggle-close {
display: none;
}
@media screen and (max-width: $screen-desktop-min) {
height: 5rem;
padding: 0 0.5rem;
.logo {
padding-left: 0;
img {
height: 3rem;
}
}
}
}
main {
width: 100%;
@media screen and (max-width: $screen-desktop-min) {
padding: 3rem 1rem;
gap: 3rem;
.card {
width: 100%;
}
}
}
footer {
z-index: 10;
padding: 1rem 0;
--paragraph-margin: 0.5rem;
--zone-color: #aaa;
min-height: 6rem;
border-top: var(--border-large);
background: var(--footer-bg);
color: var(--footer-fg);
display: grid;
place-content: center;
font-family: var(--font-secondary);
font-size: 18px;
scroll-snap-align: end;
@media screen and (max-width: $screen-desktop-min) {
min-height: 5rem;
font-size: 18px;
}
}
}
@import './pages.scss';
//
// Misc
//
::-webkit-scrollbar-track:vertical {
background-color: #f0f0f0;
border-left: 2px solid #222;
border-top: 2px solid #222;
border-bottom: 2px solid #222;
}
::-webkit-scrollbar-track:horizontal {
background-color: #f0f0f0;
border-top: 2px solid #222;
border-left: 2px solid #222;
border-right: 2px solid #222;
}
::-webkit-scrollbar-thumb {
background-color: #1e6733;
border: 2px solid #222;
}
::-webkit-scrollbar-thumb:hover {
background-color: #2b8b47;
}
::-webkit-scrollbar-corner {
background-color: #f0f0f0;
// border-left: 2px solid #222;
// border-top: 2px solid #222;
}
::-webkit-scrollbar {
width: 15px;
}
::selection {
background: #0002;
}
body:has(#header-menu-toggle:checked) {
overflow: hidden;
}
//
// Utility Classes
//
.hide {
display: none !important;
}
.invisible {
opacity: 0 !important;
}
@media screen and (min-width: $screen-desktop-min) {
.mobile-only {
display: none !important;
}
}
@media screen and (max-width: $screen-desktop-min) {
.desktop-only {
display: none !important;
}
}
-879
View File
@@ -1,879 +0,0 @@
:root {
--guide-base: #a2d4f3;
--guide-darkest: color-mix(in srgb, var(--guide-base), #000 75%);
--guide-darker: color-mix(in srgb, var(--guide-base), #000 50%);
--guide-dark: color-mix(in srgb, var(--guide-base), #000 25%);
--guide-light: color-mix(in srgb, var(--guide-base), #fff 25%);
--guide-lighter: color-mix(in srgb, var(--guide-base), #fff 50%);
--guide-lightest: color-mix(in srgb, var(--guide-base), #fff 75%);
--news-base: #f8e8b1;
}
@layer page {
.homepage {
header:has(#header-menu-toggle:not(:checked)) .logo {
visibility: hidden;
}
section {
position: relative;
width: 100%;
min-height: calc(100vh - 6rem);
&:last-of-type {
min-height: calc(100vh - 10rem);
}
// display: flex;
// flex-direction: column;
// align-items: center;
& {
display: grid;
grid-auto-flow: row;
justify-items: center;
align-content: start;
gap: 2rem;
scroll-snap-align: start;
}
& > .title {
font-size: 48px;
padding-top: 4rem;
@media screen and (max-width: $screen-desktop-min) {
padding-top: 2rem;
}
}
}
.zig-zag {
z-index: 5;
position: absolute;
width: 100%;
display: flex;
&:first-child {
bottom: 100%;
}
}
section.principal {
z-index: 2;
min-height: calc(100vh - 7rem);
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
place-content: center;
gap: 4rem;
padding: 3rem 2rem 6rem;
background: var(--homepage-principal-bg);
position: relative;
.circuit-layer {
pointer-events: none;
z-index: 1;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
canvas {
width: 100%;
height: 100%;
}
}
& > .logo {
z-index: 2;
width: 32rem;
position: relative;
user-select: none;
img {
width: 100%;
// filter: drop-shadow(6px 6px 0 var(--palette-black))
// drop-shadow(4px 0 0 var(--palette-black)) drop-shadow(0 4px 0 var(--palette-black))
// drop-shadow(-4px 0 0 var(--palette-black)) drop-shadow(0 -4px 0 var(--palette-black));
}
max-width: calc(100vw - 3rem);
@media screen and (max-width: $screen-desktop-min) {
}
}
& > .whats-phc {
z-index: 2;
background: #e4c5ff;
--zone-color: color-mix(in lab, #e4c5ff, #000 75%);
max-width: 37rem;
.title {
text-align: center;
}
a {
text-decoration: underline 2px solid;
&:hover {
--zone-color: color-mix(in lab, #e4c5ff, #000 60%);
}
}
span.highlighted {
background: color-mix(in lab, #e4c5ff, #000 10%);
cursor: default;
border-radius: 0.25rem;
padding: 0.125rem 0.25rem;
}
}
}
section.news {
--card-base: color-mix(in lab, var(--news-base), #fff 25%);
--zone-color: color-mix(in lab, var(--news-base), #000 50%);
background: var(--homepage-news-bg);
gap: 3rem;
padding-bottom: 6rem;
& > .news-list {
display: flex;
flex-direction: row;
gap: 3rem;
align-items: flex-start;
padding: 0 3rem;
justify-content: center;
flex-wrap: wrap;
}
[role='button'] {
padding: 0.5rem 2rem;
&.primary {
background: #ffdd6e;
color: #000d;
}
}
}
section.projects {
background: var(--homepage-projects-bg);
padding-bottom: 6rem;
.project-list {
// width: calc(20rem * 3 + 1.5rem * 2 + 6rem * 2);
// max-width: calc(100vw - 2rem); // HACK: capire come si propagano le width per bene
max-width: calc(20rem * 3 + 1.5rem * 2 + 1rem * 2);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
grid-auto-flow: dense;
gap: 1.5rem;
padding: 0 1rem;
& > * {
grid-row: span var(--masonry-height);
}
.project {
width: 100%;
height: 100%;
// background: #fcddff;
// background: #ffa89c;
background: var(--card-bg, var(--project-card-bg));
color: var(--card-fg, #000e);
@include neo-brutalist-card($size: 3px, $offset: 9px);
padding: 1rem;
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: auto 1fr;
gap: 0.25rem 1rem;
transition: all 128ms ease-out;
.title {
font-size: 32px;
@media screen and (max-width: $screen-desktop-min) {
font-size: 24px;
}
}
.image {
grid-row: span 2;
// place-self: center;
.box {
background: #0003;
border: 3px solid #0006;
border-radius: 6px;
width: 4rem;
height: 4rem;
}
img {
object-fit: cover;
width: 4rem;
}
// &.auto {
// img {
// width: auto;
// height: auto;
// }
// }
}
.description {
font-size: 16px;
@extend .text;
}
&:hover {
transform: translate(0, -4px);
box-shadow: 9px 13px 0 0 #222;
}
}
@media screen and (max-width: $screen-desktop-min) {
padding: 0 1rem;
grid-template-columns: 1fr;
.project {
padding: 0.8rem;
}
}
}
}
section.wanna-be-macchinista {
background: var(--homepage-macchinisti-bg);
color: #fdfdfd;
padding-bottom: 6rem;
.card {
max-width: 40rem;
}
.content {
@extend .text;
}
}
@media screen and (max-width: $screen-desktop-min) {
& > main {
padding: 0 !important;
}
section.principal {
padding: 3rem 0 6rem;
.whats-phc {
padding: 1.5rem;
margin: 0 1rem;
}
#mobile-whats-phc-read-more {
&:checked ~ .text {
max-height: 7lh;
overflow: hidden;
position: relative;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3lh;
background: linear-gradient(to bottom, transparent, #e4c5ff);
}
}
&:not(:checked) ~ * .button span:nth-child(1) {
display: none;
}
&:checked ~ * .button span:nth-child(2) {
display: none;
}
}
}
section.news {
& > .news-list {
padding: 0 1rem;
}
}
section.projects {
.project-list {
padding: 0 1rem;
}
}
section.wanna-be-macchinista {
.content {
padding: 0 1rem;
}
}
section {
padding: 1rem 1rem 4rem;
}
}
}
.utenti {
background: #ffffe4;
--filter-bg-color: #ffd270;
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem 0;
gap: 5rem;
max-width: 80ch;
.search-result {
background: #ffeabc;
}
button {
background: #ffd270;
}
}
}
.appunti {
main {
background: #e8e8e8;
justify-self: center;
display: grid;
grid-auto-flow: row;
justify-items: center;
padding: 3rem;
gap: 3rem;
width: 100%;
position: relative;
&::after {
content: '';
width: 100%;
height: 80vh;
background: linear-gradient(to bottom, transparent, #e8e8e8);
position: absolute;
bottom: 0;
left: 0;
}
.search {
max-width: 80ch;
}
.appunti-scrollable {
justify-self: stretch;
&.center {
justify-self: center;
}
}
}
}
.domande-esami {
background: hsl(180, 100%, 95%);
--card-base: hsl(180, 100%, 85%);
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 4rem 0;
gap: 2rem;
.search {
max-width: 80ch;
}
button {
background: hsl(180, 100%, 72%);
}
.card a {
color: color-mix(in srgb, var(--card-base-internal), #000 80%);
}
}
}
// .login {
// background: #ddfaff;
// main {
// justify-self: center;
// display: flex;
// flex-direction: column;
// align-items: center;
// max-width: 80ch;
// padding: 3rem 0;
// gap: 3rem;
// h3 {
// font-size: 28px;
// font-weight: 600;
// }
// }
// }
//
// Notizie
//
.notizie,
.notizia {
--card-base: color-mix(in lab, var(--news-base), #fff 25%);
--zone-color: color-mix(in lab, var(--news-base), #000 75%);
background: color-mix(in lab, var(--news-base), #fff 90%);
}
.notizie {
h1 > a {
&:hover {
text-decoration: underline 3px solid;
}
}
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem;
gap: 5rem;
}
}
.notizia {
main {
--card-base: color-mix(in lab, var(--news-base), #fff 25%);
max-width: calc(46rem + 2rem * 2);
justify-self: center;
padding: 3rem 2rem 2rem;
margin-top: 3rem;
margin-bottom: 6rem;
@media screen and (max-width: $screen-desktop-min) {
box-shadow: none;
border: none;
border-radius: 0;
max-width: none;
margin: 0 auto;
}
}
}
//
// Guide
//
.guide,
.guida {
--card-base: color-mix(in lab, var(--guide-base), #fff 25%);
--zone-color: color-mix(in lab, var(--guide-base), #000 75%);
background: color-mix(in lab, var(--guide-base), #fff 90%);
}
.guide {
h1 > a {
&:hover {
text-decoration: underline 3px solid;
}
}
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem;
gap: 5rem;
}
}
.guida {
main {
--card-base: color-mix(in lab, var(--guide-base), #fff 50%);
max-width: calc(46rem + 2rem * 2);
justify-self: center;
padding: 3rem 2rem 2rem;
margin-top: 3rem;
margin-bottom: 6rem;
position: relative;
.metadata {
position: absolute;
top: -2px;
left: calc(100% + 2rem);
width: 15rem;
display: grid;
grid-auto-flow: row;
gap: 1rem;
justify-items: start;
.metadata-item {
background: var(--card-base);
display: grid;
grid-template-rows: auto auto;
justify-items: start;
gap: 0.25rem;
padding: 0.75rem 1rem;
@include neo-brutalist-card($size: 3px);
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
}
}
@media screen and (max-width: $screen-desktop-min) {
box-shadow: none;
border: none;
border-radius: 0;
max-width: none;
margin: 0 auto;
.metadata {
position: static;
padding: 0;
width: auto;
margin: 0;
--card-base: color-mix(in lab, var(--guide-base), #fff 30%);
}
}
}
}
.tag {
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem 0;
gap: 5rem;
}
}
.storia {
--card-base: #e4c5ff;
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem 0;
gap: 3rem;
// background horizontal linear gradient that is black in the center
background: linear-gradient(
to right,
#ffe4c544 0%,
// #ffe4c599 25%,
#ffe4c5ff 50%,
// #ffe4c599 75%,
#ffe4c544 100%
);
}
}
.macchinisti {
main {
justify-self: center;
background: linear-gradient(to top, #d5fbff, #ffd9d5);
display: flex;
flex-direction: column;
align-items: center;
padding: 4.5rem 3rem;
gap: 4.5rem;
}
}
//
// Meta
//
.design {
grid-template-columns: minmax(15rem, 2fr) 10fr;
@media screen and (max-width: 1400px) {
grid-template-columns: 1fr;
}
aside {
margin: 1rem;
padding: 1rem;
@include neo-brutalist-card();
background: #f0f0f0;
align-self: start;
position: sticky;
top: 7rem;
height: calc(100dvh - 8rem - 6rem);
nav {
display: flex;
flex-direction: column;
gap: 0.25rem;
ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.25rem;
li {
padding-left: calc((var(--depth) - 1) * 1rem);
display: flex;
a {
display: block;
font-size: 16px;
font-weight: 600;
transform: translate(-0.25rem, 0);
padding: 0.125rem 0.25rem;
border-radius: 0.125rem;
color: #444;
&:hover {
background: #00000018;
}
}
}
}
}
@media screen and (max-width: 1400px) {
display: none;
}
}
footer {
grid-column: 1 / -1;
}
main {
justify-self: center;
@media screen and (min-width: $screen-desktop-min) {
padding: 3rem 0;
}
}
pre[data-language='astro'] {
--code-bg: #fff7ef;
width: 100%;
max-width: 46rem;
}
.container {
margin: 2rem auto;
border: 2px dashed #ddd;
position: relative;
z-index: 1;
&.large {
min-width: calc(100% - 4rem);
}
&:not(.large) {
& > .content {
display: grid;
place-content: center;
}
}
& > .content {
padding: 2rem;
overflow: auto;
}
// label in the top left corner
&::before {
content: 'Preview';
position: absolute;
bottom: 100%;
left: 0;
padding: 0.125rem 0.5rem;
background: #eee;
color: #000;
font-family: var(--font-display);
font-size: 14px;
border-radius: 0.25rem;
z-index: -1;
transform: translate(-2px, -4px);
opacity: 0;
transition: opacity 64ms ease-in;
}
&:hover {
border-color: #bbb;
&::before {
opacity: 1;
}
}
}
.palette {
margin: 2rem auto;
display: grid;
grid-template-columns: auto auto;
gap: 1rem;
place-content: center;
& > .color {
width: 2rem;
height: 2rem;
border-radius: 0.25rem;
border: 2px solid #000;
box-shadow: 4px 4px 0 0 #000;
overflow: hidden;
& > .region {
width: 100%;
height: 100%;
border: 2px solid #fff;
border-radius: 4px;
}
}
& > .label {
display: grid;
align-content: center;
font-family: 'JetBrains Mono', var(--font-mono);
font-size: 16px;
user-select: all;
}
}
}
}
+55
View File
@@ -0,0 +1,55 @@
@layer page {
main {
background: #e8e8e8;
justify-self: center;
display: grid;
grid-auto-flow: row;
justify-items: center;
padding: 3rem;
gap: 3rem;
width: 100%;
position: relative;
&::after {
content: '';
width: 100%;
height: 80vh;
background: linear-gradient(to bottom, transparent, #e8e8e8);
position: absolute;
bottom: 0;
left: 0;
}
.search {
max-width: 80ch;
}
.appunti-scrollable {
justify-self: stretch;
&.center {
justify-self: center;
}
}
@media screen and (max-width: 1024px) {
padding: 3rem 1rem;
gap: 3rem;
overflow: clip;
.card {
width: 100%;
}
}
}
}
+42
View File
@@ -0,0 +1,42 @@
:root {
--card-base: hsl(180, 100%, 85%);
}
@layer page {
body {
background: hsl(180, 100%, 95%);
}
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 4rem 0;
gap: 2rem;
.search {
max-width: 80ch;
}
button {
background: hsl(180, 100%, 72%);
}
.card a {
color: color-mix(in srgb, var(--card-base-internal), #000 80%);
}
@media screen and (max-width: 1024px) {
padding: 3rem 1rem;
gap: 3rem;
.card {
width: 100%;
}
}
}
}
+74
View File
@@ -0,0 +1,74 @@
@layer page {
body {
--card-base: color-mix(in lab, var(--guide-base), #fff 25%);
--zone-color: color-mix(in lab, var(--guide-base), #000 75%);
background: color-mix(in lab, var(--guide-base), #fff 90%);
}
main {
--card-base: color-mix(in lab, var(--guide-base), #fff 50%);
max-width: calc(46rem + 2rem * 2);
justify-self: center;
padding: 3rem 2rem 2rem;
margin-top: 3rem;
margin-bottom: 6rem;
position: relative;
.metadata {
position: absolute;
top: -2px;
left: calc(100% + 2rem);
width: 15rem;
display: grid;
grid-auto-flow: row;
gap: 1rem;
justify-items: start;
.metadata-item {
background: var(--card-base);
display: grid;
grid-template-rows: auto auto;
justify-items: start;
gap: 0.25rem;
padding: 0.75rem 1rem;
/* @include neo-brutalist-card($size: 3px); */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 3px 3px 0 0 #222;
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
}
}
@media screen and (max-width: 1024px) {
box-shadow: none;
border: none;
border-radius: 0;
max-width: none;
margin: 0 auto;
padding: 3rem 1rem 2rem;
.metadata {
position: static;
padding: 0;
width: auto;
margin: 0;
--card-base: color-mix(in lab, var(--guide-base), #fff 30%);
}
}
}
}
+37
View File
@@ -0,0 +1,37 @@
@layer page {
body {
--card-base: color-mix(in lab, var(--guide-base), #fff 25%);
--zone-color: color-mix(in lab, var(--guide-base), #000 75%);
background: color-mix(in lab, var(--guide-base), #fff 90%);
}
h1 > a {
&:hover {
text-decoration: underline 3px solid;
}
}
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem 2.5rem;
gap: 5rem;
@media screen and (max-width: 1024px) {
padding: 3rem 1rem;
gap: 3rem;
.card {
width: 100%;
}
}
}
.card {
grid-template-rows: auto 1fr auto;
}
}
+354
View File
@@ -0,0 +1,354 @@
@layer page {
header:has(#header-menu-toggle:not(:checked)) .logo {
visibility: hidden;
}
section {
position: relative;
width: 100%;
min-height: calc(100vh - 6rem);
&:last-of-type {
min-height: calc(100vh - 10rem);
}
/* display: flex;
flex-direction: column;
align-items: center; */
& {
display: grid;
grid-auto-flow: row;
justify-items: center;
align-content: start;
gap: 2rem;
scroll-snap-align: start;
}
& > .title {
font-size: 48px;
padding-top: 4rem;
@media screen and (max-width: /* $screen-desktop-min */ 1024px) {
padding-top: 2rem;
}
}
}
.zig-zag {
z-index: 5;
position: absolute;
width: 100%;
display: flex;
&:first-child {
bottom: 100%;
}
}
section.principal {
z-index: 2;
min-height: calc(100vh - 7rem);
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
place-content: center;
gap: 4rem;
padding: 3rem 2rem 6rem;
background: var(--homepage-principal-bg);
position: relative;
.circuit-layer {
pointer-events: none;
z-index: 1;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
canvas {
width: 100%;
height: 100%;
}
}
& > .logo {
z-index: 2;
width: 32rem;
position: relative;
user-select: none;
img {
width: 100%;
/* filter: drop-shadow(6px 6px 0 var(--palette-black))
drop-shadow(4px 0 0 var(--palette-black)) drop-shadow(0 4px 0 var(--palette-black))
drop-shadow(-4px 0 0 var(--palette-black)) drop-shadow(0 -4px 0 var(--palette-black)); */
}
max-width: calc(100vw - 3rem);
@media screen and (max-width: /* $screen-desktop-min */ 1024px) {
}
}
& > .whats-phc {
z-index: 2;
background: #e4c5ff;
--zone-color: color-mix(in lab, #e4c5ff, #000 75%);
max-width: 37rem;
.title {
text-align: center;
}
a {
text-decoration: underline 2px solid;
&:hover {
--zone-color: color-mix(in lab, #e4c5ff, #000 60%);
}
}
span.highlighted {
background: color-mix(in lab, #e4c5ff, #000 10%);
cursor: default;
border-radius: 0.25rem;
padding: 0.125rem 0.25rem;
}
}
}
section.news {
--card-base: color-mix(in lab, var(--news-base), #fff 25%);
--zone-color: color-mix(in lab, var(--news-base), #000 50%);
background: var(--homepage-news-bg);
gap: 3rem;
padding-bottom: 6rem;
& > .news-list {
display: flex;
flex-direction: row;
gap: 3rem;
align-items: flex-start;
padding: 0 3rem;
justify-content: center;
flex-wrap: wrap;
}
[role='button'] {
padding: 0.5rem 2rem;
&.primary {
background: #ffdd6e;
color: #000d;
}
}
}
section.projects {
background: var(--homepage-projects-bg);
padding-bottom: 6rem;
.project-list {
/* width: calc(20rem * 3 + 1.5rem * 2 + 6rem * 2);
max-width: calc(100vw - 2rem); */ /* HACK: capire come si propagano le width per bene */
max-width: calc(20rem * 3 + 1.5rem * 2 + 1rem * 2);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
grid-auto-flow: dense;
gap: 1.5rem;
padding: 0 1rem;
& > * {
grid-row: span var(--masonry-height);
}
.project {
width: 100%;
height: 100%;
/* background: #fcddff;
background: #ffa89c; */
background: var(--card-bg, var(--project-card-bg));
color: var(--card-fg, #000e);
/* @include neo-brutalist-card($size: 3px, $offset: 9px); */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 9px 9px 0 0 #222;
padding: 1rem;
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: auto 1fr;
gap: 0.25rem 1rem;
transition: all 128ms ease-out;
.title {
font-size: 32px;
@media screen and (max-width: /* $screen-desktop-min */ 1024px) {
font-size: 24px;
}
}
.image {
grid-row: span 2;
/* place-self: center; */
.box {
background: #0003;
border: 3px solid #0006;
border-radius: 6px;
width: 4rem;
height: 4rem;
}
img {
object-fit: cover;
width: 4rem;
}
/* &.auto {
img {
width: auto;
height: auto;
}
} */
}
.description {
font-size: 16px;
}
&:hover {
transform: translate(0, -4px);
box-shadow: 9px 13px 0 0 #222;
}
}
@media screen and (max-width: /* $screen-desktop-min */ 1024px) {
padding: 0 1rem;
grid-template-columns: 1fr;
.project {
padding: 0.8rem;
}
}
}
}
section.wanna-be-macchinista {
background: var(--homepage-macchinisti-bg);
color: #fdfdfd;
padding-bottom: 6rem;
.card {
max-width: 40rem;
}
/* .content {
/* @extend .text;
/* Placeholder: Assuming .text is a class with common text styles, you might want to define those styles directly here or in a separate CSS rule.
} */
}
@media screen and (max-width: /* $screen-desktop-min */ 1024px) {
main {
padding: 0 !important;
}
section.principal {
padding: 3rem 0 6rem;
.whats-phc {
padding: 1.5rem;
margin: 0 1rem;
}
#mobile-whats-phc-read-more {
&:checked ~ .text {
max-height: 7lh;
overflow: hidden;
position: relative;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3lh;
background: linear-gradient(to bottom, transparent, #e4c5ff);
}
}
&:not(:checked) ~ * .button span:nth-child(1) {
display: none;
}
&:checked ~ * .button span:nth-child(2) {
display: none;
}
}
}
section.news {
& > .news-list {
padding: 0 1rem;
}
}
section.projects {
.project-list {
padding: 0 1rem;
}
}
section.wanna-be-macchinista {
.content {
padding: 0 1rem;
}
}
section {
padding: 1rem 1rem 4rem;
}
}
}
+22
View File
@@ -0,0 +1,22 @@
@layer page {
main {
justify-self: center;
background: linear-gradient(to top, #d5fbff, #ffd9d5);
display: flex;
flex-direction: column;
align-items: center;
padding: 4.5rem 3rem;
gap: 4.5rem;
@media screen and (max-width: 1024px) {
padding: 3rem 1rem;
gap: 3rem;
.card {
width: 100%;
}
}
}
}
File diff suppressed because it is too large Load Diff
+190
View File
@@ -0,0 +1,190 @@
@layer page {
body {
grid-template-columns: minmax(15rem, 2fr) 10fr;
@media screen and (max-width: 1400px) {
grid-template-columns: 1fr;
}
}
aside {
margin: 1rem;
padding: 1rem;
/* @include neo-brutalist-card(); */
border: 3px solid #222;
border-radius: 6px;
box-shadow: 3px 3px 0 0 #222;
background: #f0f0f0;
align-self: start;
position: sticky;
top: 7rem;
height: calc(100dvh - 8rem - 6rem);
nav {
display: flex;
flex-direction: column;
gap: 0.25rem;
ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.25rem;
li {
padding-left: calc((var(--depth) - 1) * 1rem);
display: flex;
a {
display: block;
font-size: 16px;
font-weight: 600;
transform: translate(-0.25rem, 0);
padding: 0.125rem 0.25rem;
border-radius: 0.125rem;
color: #444;
&:hover {
background: #00000018;
}
}
}
}
}
@media screen and (max-width: 1400px) {
display: none;
}
}
footer {
grid-column: 1 / -1;
}
main {
justify-self: center;
@media screen and (min-width: /* $screen-desktop-min */ 1024px) {
padding: 3rem 0;
}
}
pre[data-language='astro'] {
--code-bg: #fff7ef;
width: 100%;
max-width: 46rem;
}
.container {
margin: 2rem auto;
border: 2px dashed #ddd;
position: relative;
z-index: 1;
&.large {
min-width: calc(100% - 4rem);
}
&:not(.large) {
& > .content {
display: grid;
place-content: center;
}
}
& > .content {
padding: 2rem;
overflow: auto;
}
/* label in the top left corner */
&::before {
content: 'Preview';
position: absolute;
bottom: 100%;
left: 0;
padding: 0.125rem 0.5rem;
background: #eee;
color: #000;
font-family: var(--font-display);
font-size: 14px;
border-radius: 0.25rem;
z-index: -1;
transform: translate(-2px, -4px);
opacity: 0;
transition: opacity 64ms ease-in;
}
&:hover {
border-color: #bbb;
&::before {
opacity: 1;
}
}
}
.palette {
margin: 2rem auto;
display: grid;
grid-template-columns: auto auto;
gap: 1rem;
place-content: center;
& > .color {
width: 2rem;
height: 2rem;
border-radius: 0.25rem;
border: 2px solid #000;
box-shadow: 4px 4px 0 0 #000;
overflow: hidden;
& > .region {
width: 100%;
height: 100%;
border: 2px solid #fff;
border-radius: 4px;
}
}
& > .label {
display: grid;
align-content: center;
font-family: 'JetBrains Mono', var(--font-mono);
font-size: 16px;
user-select: all;
}
}
}
+28
View File
@@ -0,0 +1,28 @@
@layer page {
:root {
--card-base: color-mix(in lab, var(--news-base), #fff 25%);
--zone-color: color-mix(in lab, var(--news-base), #000 75%);
}
body {
background: color-mix(in lab, var(--news-base), #fff 90%);
}
main {
max-width: calc(46rem + 2rem * 2);
justify-self: center;
padding: 3rem 2rem 2rem;
margin-top: 3rem;
margin-bottom: 6rem;
@media screen and (max-width: 1024px) {
box-shadow: none;
border: none;
border-radius: 0;
max-width: none;
margin: 0 auto;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
@layer page {
:root {
--card-base: color-mix(in lab, var(--news-base), #fff 25%);
--zone-color: color-mix(in lab, var(--news-base), #000 75%);
}
body {
background: color-mix(in lab, var(--news-base), #fff 90%);
}
h1 > a {
&:hover {
text-decoration: underline 3px solid;
}
}
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem;
gap: 5rem;
@media screen and (max-width: 1024px) {
padding: 3rem 1rem;
gap: 3rem;
.card {
width: 100%;
}
}
}
.card {
grid-template-rows: auto auto 1fr;
}
}
+33
View File
@@ -0,0 +1,33 @@
:root {
--card-base: #e4c5ff;
}
@layer page {
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem 0;
gap: 3rem;
/* background horizontal linear gradient that is black in the center */
background: linear-gradient(
to right,
#ffe4c544 0%,
/* #ffe4c599 25%, */ #ffe4c5ff 50%,
/* #ffe4c599 75%, */ #ffe4c544 100%
);
@media screen and (max-width: 1024px) {
padding: 3rem 1rem;
gap: 3rem;
.card {
width: 100%;
}
}
}
}
+40
View File
@@ -0,0 +1,40 @@
:root {
--filter-bg-color: #ffd270;
}
@layer page {
body {
background: #ffffe4;
}
main {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
padding: 5rem 0;
gap: 5rem;
max-width: 80ch;
.search-result {
background: #ffeabc;
}
button {
background: #ffd270;
}
@media screen and (max-width: 1024px) {
padding: 3rem 1rem;
gap: 3rem;
.card {
width: 100%;
}
}
}
}
@@ -1,37 +1,8 @@
@function pow($number, $exponent) {
$value: 1;
@if $exponent > 0 {
@for $i from 1 through $exponent {
$value: $value * $number;
}
}
@return $value;
}
@mixin geometric-headings {
$base-font-size: 16px;
$heading-scale: 1.25;
@for $i from 1 through 5 {
$factor: pow($heading-scale, 5 - $i);
h#{$i} {
font-size: $base-font-size * $factor;
font-family: var(--font-display);
font-weight: 700;
margin-bottom: 0.25rem;
}
// p + h#{$i} {
// margin-top: 0.75rem * $factor;
// }
}
}
@layer typography {
@include geometric-headings;
:root {
--heading-base-size: 16px;
--heading-factor: 1.25;
}
strong {
font-weight: 600;
@@ -45,9 +16,48 @@
text-align: center;
}
h1 {
font-size: calc(var(--heading-base-size) * pow(var(--heading-factor), 4));
}
h2 {
font-size: calc(var(--heading-base-size) * pow(var(--heading-factor), 3));
}
h3 {
font-size: calc(var(--heading-base-size) * pow(var(--heading-factor), 2));
}
h4 {
font-size: calc(var(--heading-base-size) * pow(var(--heading-factor), 1));
}
h5 {
font-size: calc(var(--heading-base-size) * pow(var(--heading-factor), 0));
}
h1,
h2,
h3,
h4,
h5 {
font-family: var(--font-display);
font-weight: 700;
}
.text {
// text-align: justify;
// hyphens: auto;
h1,
h2,
h3,
h4,
h5 {
font-family: var(--font-display);
font-weight: 700;
margin-bottom: 0.25rem;
}
/* text-align: justify;
hyphens: auto; */
&.center {
text-align: center;
@@ -64,8 +74,8 @@
pre,
code {
background: color-mix(in lab, var(--card-base-internal, #ededed), #fff 35%) !important;
// background: color-mix(in lab, var(--zone-color), #fff 75%) !important;
// background: var(--code-bg, #00000022) !important;
/* background: color-mix(in lab, var(--zone-color), #fff 75%) !important;
background: var(--code-bg, #00000022) !important; */
font-family: var(--font-mono);
font-weight: 400;
@@ -77,12 +87,16 @@
padding: 0.125rem 0.3rem;
}
p > code {
word-break: break-word;
}
pre {
margin: 2rem auto;
padding: 0.5rem 1rem;
// width: 100%;
/* width: 100%; */
max-width: 80ch;
width: fit-content;
@@ -97,11 +111,6 @@
code {
padding: 0;
}
@media screen and (max-width: $screen-desktop-min) {
width: calc(100vw - 2rem);
max-width: none;
}
}
p {
@@ -109,26 +118,26 @@
margin: var(--paragraph-margin, 1rem) auto;
}
// p + p {
// margin-top: 1rem;
// }
/* p + p {
margin-top: 1rem;
}
// h1 + p,
// h2 + p,
// h3 + p,
// h4 + p {
// margin-top: 1rem;
// }
h1 + p,
h2 + p,
h3 + p,
h4 + p {
margin-top: 1rem;
}
// p:has(+ h1, + h2, + h3, + h4) {
// margin-bottom: 1rem;
// }
p:has(+ h1, + h2, + h3, + h4) {
margin-bottom: 1rem;
} */
p[align='center'] {
margin: 1.5rem 0;
a {
// background: color-mix(in hsl, var(--card-base-internal, #ededed), #fff 20%);
/* background: color-mix(in hsl, var(--card-base-internal, #ededed), #fff 20%); */
background: hsl(from var(--card-base-internal, #ededed) h calc(s + 10) calc(l - 10));
padding: 0.5rem;
border-radius: 0.25rem;
@@ -144,7 +153,10 @@
width: 50ch;
max-width: 100%;
@include neo-brutalist-card(2px);
border: 2px solid #333;
border-radius: 0.25rem;
box-shadow: 0.25rem 0.25rem 0 0 #333;
/* background: color-mix(in lab, var(--card-base-internal, #ededed), #fff 20%); */
&.fill {
width: 100%;
@@ -177,7 +189,22 @@
font-style: italic;
}
@include geometric-headings;
/* @include geometric-headings; */
/* Inlined geometric-headings mixin */
/* @for $i from 1 through 5 {
$factor: pow($heading-scale, 5 - $i);
h#{$i} {
font-size: $base-font-size * $factor;
font-family: var(--font-display);
font-weight: 700;
margin-bottom: 0.25rem;
}
/* p + h#{$i} {
margin-top: 0.75rem * $factor;
}
} */
h1 {
margin-bottom: 2rem;
@@ -191,7 +218,7 @@
font-weight: 700;
color: #333;
// HACK: Trick to fix anchor links with sticky header
/* HACK: Trick to fix anchor links with sticky header */
padding-top: 7rem;
margin-top: -6.5rem;
}
@@ -250,9 +277,17 @@
border-top: 2px solid #0003;
}
@media screen and (max-width: $screen-desktop-min) {
& > * {
margin: 0 0.75rem;
@media screen and (max-width: 1024px) {
max-width: calc(100vw - 2rem);
pre {
margin: 1rem 0;
width: 100%;
}
hr {
max-width: none;
margin: 3rem auto;
}
}
}