From 390b74526a56992da39f37c457c725454a769fb7 Mon Sep 17 00:00:00 2001 From: Pedro Monteiro <83165668+pedroafmonteiro@users.noreply.github.com> Date: Tue, 1 Sep 2026 18:43:54 +0100 Subject: [PATCH 01/33] feat: new navbar, home, footer for 2026 --- package.json | 4 +- public/images/sinf-2026-sm.svg | 3 + resources/css/app.css | 219 +++++++- resources/js/Components/Dropdown.vue | 114 +++-- resources/js/Components/DropdownLink.vue | 33 +- resources/js/Components/DropdownTrigger.vue | 11 +- resources/js/Components/Footer.vue | 172 +++++-- resources/js/Components/HamburgerMenu.vue | 425 ++++++++++++---- .../js/Components/Home/EnrollSection.vue | 49 +- resources/js/Components/Home/SpeakerSlide.vue | 164 ++++-- .../js/Components/Home/SpeakersCarousel.vue | 153 ++++-- resources/js/Components/Home/Sponsor.vue | 110 ++-- .../js/Components/Home/SponsorBanner.vue | 136 +---- resources/js/Components/NavLink.vue | 22 +- resources/js/Components/Navbar.vue | 468 ++++++++++++------ resources/js/Components/ResponsiveNavLink.vue | 23 +- resources/js/Layouts/AppLayout.vue | 39 +- resources/js/Pages/Home.vue | 460 ++++++++--------- resources/js/Pages/Maintenance.vue | 163 +++--- resources/views/app.blade.php | 5 +- .../views/vendor/mail/html/themes/default.css | 2 +- 21 files changed, 1815 insertions(+), 960 deletions(-) create mode 100644 public/images/sinf-2026-sm.svg diff --git a/package.json b/package.json index 71a91bb6..43b61d20 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "scripts": { "dev": "vite", "build": "vite build", - "format": "prettier --check *.js *.ts resources/js/", - "format:fix": "prettier --write *.js *.ts resources/js/", + "format": "prettier --check vite.config.ts resources/js/ resources/css/", + "format:fix": "prettier --write vite.config.ts resources/js/ resources/css/", "lint": "eslint resources/js/", "lint:fix": "eslint --fix resources/js/", "type-check": "vue-tsc --noEmit -p tsconfig.app.json" diff --git a/public/images/sinf-2026-sm.svg b/public/images/sinf-2026-sm.svg new file mode 100644 index 00000000..4f593613 --- /dev/null +++ b/public/images/sinf-2026-sm.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/css/app.css b/resources/css/app.css index e4a081bf..e349bc63 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,18 +1,30 @@ -@import 'tailwindcss'; +@import "tailwindcss"; @plugin "@tailwindcss/forms"; @plugin "@tailwindcss/typography"; @theme { - --font-sans: "Figtree", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + --font-sans: + "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, - "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", + "Noto Color Emoji"; --color-text-color: #ffffff; - --color-2025-bg-green: #59B672; - --color-2025-blue: #3A9699; - --color-2025-bg: #1A74C2; - --color-2025-blue-dark: #0F3D57; + + /* 2026 Theme: Core Brand & Ambient Glow Palette */ + --color-sinf-crimson: rgb(154, 0, 54); + --color-sinf-crimson-dark: rgb(64, 0, 23); + --color-sinf-purple: rgb(106, 0, 255); + --color-sinf-purple-dark: rgb(34, 1, 153); + --color-sinf-surface: #111215; + --color-sinf-surface-dark: #090a0c; + + /* Previous Editions Theme Colors */ + --color-2025-bg-green: #59b672; + --color-2025-blue: #3a9699; + --color-2025-bg: #1a74c2; + --color-2025-blue-dark: #0f3d57; --color-2023-bg: rgb(248, 245, 231); --color-2023-red: rgb(217, 79, 4); --color-2023-red-dark: rgb(177, 93, 93); @@ -20,17 +32,41 @@ --color-2023-teal-dark: rgb(2, 82, 89); --color-2023-orange: rgb(242, 147, 37); - --background-image-2025-bg-gradient: linear-gradient(45deg, #0f3d56 0%, #1A74C2 80%, #59B672 100%); + --background-image-2025-bg-gradient: linear-gradient( + 45deg, + #0f3d56 0%, + #1a74c2 80%, + #59b672 100% + ); - --shadow-sm: 2px 2px 0 -1px var(--tw-shadow-color, currentColor), 2px 2px 0 0 black; - --shadow: 4px 4px 0 -1px var(--tw-shadow-color, currentColor), 4px 4px 0 0 black; - --shadow-md: 6px 6px 0 -1px var(--tw-shadow-color, currentColor), 6px 6px 0 0 black; - --shadow-lg: 8px 8px 0 -1px var(--tw-shadow-color, currentColor), 8px 8px 0 0 black; - --shadow-xl: 12px 12px 0 -1px var(--tw-shadow-color, currentColor), 12px 12px 0 0 black; - --shadow-2xl: 16px 16px 0 -1px var(--tw-shadow-color, currentColor), 16px 16px 0 0 black; + /* Shadows & Inset Highlights */ + --shadow-sm: + 2px 2px 0 -1px var(--tw-shadow-color, currentColor), 2px 2px 0 0 black; + --shadow: + 4px 4px 0 -1px var(--tw-shadow-color, currentColor), 4px 4px 0 0 black; + --shadow-md: + 6px 6px 0 -1px var(--tw-shadow-color, currentColor), 6px 6px 0 0 black; + --shadow-lg: + 8px 8px 0 -1px var(--tw-shadow-color, currentColor), 8px 8px 0 0 black; + --shadow-xl: + 12px 12px 0 -1px var(--tw-shadow-color, currentColor), + 12px 12px 0 0 black; + --shadow-2xl: + 16px 16px 0 -1px var(--tw-shadow-color, currentColor), + 16px 16px 0 0 black; - --animate-2023-maintenance-jump: 2023-jump 2s cubic-bezier(0.46, 0.03, 0.52, 0.96) - infinite; + --shadow-pill-inset: inset 0 1px 1px rgba(255, 255, 255, 0.05); + --shadow-pill-active: inset 0 1px 0 0 rgba(255, 255, 255, 0.25); + --shadow-dropdown: 0 20px 50px rgba(0, 0, 0, 0.8); + + /* Keyframe Animations */ + --animate-2023-maintenance-jump: 2023-jump 2s + cubic-bezier(0.46, 0.03, 0.52, 0.96) infinite; + --animate-float-crimson-1: float-crimson-1 10s ease-in-out infinite; + --animate-float-purple-1: float-purple-1 12s ease-in-out infinite; + --animate-float-crimson-2: float-crimson-2 9s ease-in-out infinite; + --animate-float-purple-2: float-purple-2 11s ease-in-out infinite; + --animate-spin-slow: spin 25s linear infinite; --breakpoint-ml: 900px; --breakpoint-xs: 500px; @@ -46,6 +82,157 @@ } } +@keyframes float-crimson-1 { + 0%, + 100% { + transform: translate3d(0, 0, 0) scale(1) rotate(0deg); + opacity: 0.85; + } + 33% { + transform: translate3d(-18vw, 12vh, 0) scale(1.2) rotate(45deg); + opacity: 1; + } + 66% { + transform: translate3d(-8vw, -15vh, 0) scale(0.9) rotate(-30deg); + opacity: 0.75; + } +} + +@keyframes float-purple-1 { + 0%, + 100% { + transform: translate3d(0, 0, 0) scale(1) rotate(0deg); + opacity: 0.8; + } + 33% { + transform: translate3d(18vw, -14vh, 0) scale(0.85) rotate(-45deg); + opacity: 0.65; + } + 66% { + transform: translate3d(10vw, 16vh, 0) scale(1.25) rotate(35deg); + opacity: 0.95; + } +} + +@keyframes float-crimson-2 { + 0%, + 100% { + transform: translate3d(0, 0, 0) scale(1); + opacity: 0.7; + } + 50% { + transform: translate3d(-20vw, -18vh, 0) scale(1.3); + opacity: 0.95; + } +} + +@keyframes float-purple-2 { + 0%, + 100% { + transform: translate3d(0, 0, 0) scale(1); + opacity: 0.65; + } + 50% { + transform: translate3d(20vw, 18vh, 0) scale(1.3); + opacity: 0.9; + } +} + +@utility pill-container { + display: flex; + align-items: center; + border-radius: 9999px; + border: 1px solid rgba(255, 255, 255, 0.08); + background-color: rgba(0, 0, 0, 0.5); + padding: 0.25rem; + box-shadow: var(--shadow-pill-inset); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); +} + +@utility pill-item { + display: inline-flex; + align-items: center; + gap: 0.375rem; + border-radius: 9999px; + padding: 0.375rem 0.875rem; + font-size: 13px; + font-weight: 500; + color: #d4d4d4; + transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + background-color: rgba(255, 255, 255, 0.08); + color: #ffffff; + } + + &:active { + transform: scale(0.95); + } + + &:focus { + outline: none; + } +} + +@utility pill-item-active { + background-color: rgba(255, 255, 255, 0.14); + font-weight: 600; + color: #ffffff; + box-shadow: var(--shadow-pill-active); +} + +@utility glass-dropdown { + border-radius: 1rem; + border: 1px solid rgba(255, 255, 255, 0.1); + background-color: rgba(17, 18, 21, 0.95); + box-shadow: var(--shadow-dropdown); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); +} + +@utility glow-orb-crimson-1 { + background: radial-gradient( + ellipse 70% 60% at 50% 50%, + rgb(154 0 54 / 100%) 0%, + rgb(154 0 54 / 60%) 40%, + rgb(64 0 23 / 25%) 70%, + transparent 100% + ); + animation: var(--animate-float-crimson-1); +} + +@utility glow-orb-purple-1 { + background: radial-gradient( + ellipse 70% 60% at 50% 50%, + rgb(106 0 255 / 95%) 0%, + rgb(106 0 255 / 55%) 40%, + rgb(34 1 153 / 25%) 70%, + transparent 100% + ); + animation: var(--animate-float-purple-1); +} + +@utility glow-orb-crimson-2 { + background: radial-gradient( + ellipse 80% 50% at 50% 50%, + rgb(154 0 54 / 85%) 0%, + rgb(64 0 23 / 35%) 50%, + transparent 100% + ); + animation: var(--animate-float-crimson-2); +} + +@utility glow-orb-purple-2 { + background: radial-gradient( + ellipse 80% 50% at 50% 50%, + rgb(106 0 255 / 85%) 0%, + rgb(34 1 153 / 35%) 50%, + transparent 100% + ); + animation: var(--animate-float-purple-2); +} + @layer base { *, ::after, diff --git a/resources/js/Components/Dropdown.vue b/resources/js/Components/Dropdown.vue index 209bf1e3..175e7c78 100644 --- a/resources/js/Components/Dropdown.vue +++ b/resources/js/Components/Dropdown.vue @@ -2,59 +2,115 @@ import { computed, onMounted, onUnmounted, ref } from "vue"; interface Props { + align?: "left" | "right" | "center"; width?: string | number; - contentClasses?: string[]; + contentClasses?: string; } const props = withDefaults(defineProps(), { + align: "center", width: "48", - contentClasses: () => ["text-2023-red-dark", "text-center"], + contentClasses: "p-1.5", }); -let open = ref(false); +const open = ref(false); +let closeTimer: ReturnType | null = null; + +const onMouseEnter = () => { + if (closeTimer) { + clearTimeout(closeTimer); + closeTimer = null; + } + open.value = true; +}; + +const onMouseLeave = () => { + closeTimer = setTimeout(() => { + open.value = false; + }, 150); +}; + +const toggle = () => { + open.value = !open.value; +}; + +const close = () => { + open.value = false; +}; const closeOnEscape = (e: KeyboardEvent) => { if (open.value && e.key === "Escape") open.value = false; }; -onMounted(() => document.addEventListener("keydown", closeOnEscape)); -onUnmounted(() => document.removeEventListener("keydown", closeOnEscape)); +const handleClickOutside = (e: MouseEvent) => { + const target = e.target as HTMLElement | null; + if (!target?.closest("[data-dropdown-root]")) { + open.value = false; + } +}; + +onMounted(() => { + document.addEventListener("keydown", closeOnEscape); + document.addEventListener("click", handleClickOutside); +}); + +onUnmounted(() => { + document.removeEventListener("keydown", closeOnEscape); + document.removeEventListener("click", handleClickOutside); +}); const widthClass = computed(() => { - return { - "20": "w-20", - "32": "w-32", - "48": "w-48", - }[props.width.toString()]; + return ( + { + "20": "w-20", + "32": "w-32", + "48": "w-48", + "52": "w-52", + "56": "w-56", + "64": "w-64", + }[props.width.toString()] || "w-48" + ); +}); + +const alignmentClasses = computed(() => { + if (props.align === "left") { + return "left-0"; + } else if (props.align === "right") { + return "right-0"; + } + return "left-1/2 -translate-x-1/2"; }); diff --git a/resources/js/Components/DropdownLink.vue b/resources/js/Components/DropdownLink.vue index 833286fa..77680d24 100644 --- a/resources/js/Components/DropdownLink.vue +++ b/resources/js/Components/DropdownLink.vue @@ -1,4 +1,5 @@