Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ed91afa
chore: add develop to ci
itziarZG Feb 3, 2026
f169a03
Merge branch 'develop' of https://github.com/python-spain/2026.es.pyc…
itziarZG Feb 3, 2026
c152206
Feat: Menú dinámico con comportamiento estándard por teclado #63 (#66)
itziarZG Feb 3, 2026
c965990
chore(merge): merge main into develop
ctrl-alt-d Feb 8, 2026
e4cd85e
Merge branch 'main' into develop
ctrl-alt-d Feb 10, 2026
1fabb2d
Merge branch 'main' into develop
itziarZG Feb 13, 2026
5990e9f
refactor: transforma home en secciones y añade un mensaje (#99)
francescarpi Feb 17, 2026
514e9fd
feat: add navigation to header (#100)
itziarZG Feb 17, 2026
ab44006
Merge branch 'main' into develop
francescarpi Feb 17, 2026
ca48bce
Merge branch 'main' into develop
ctrl-alt-d Feb 24, 2026
80c4899
Merge branch 'main' into develop
ctrl-alt-d Feb 24, 2026
7ba629e
Feat: Menu item paquetes patro
ctrl-alt-d Feb 24, 2026
bc84271
Sección de patrocinadores en la home (#104)
francescarpi Feb 26, 2026
9f24f8c
Feat: Merge branch 'main' into develop
ctrl-alt-d Feb 26, 2026
116d5c0
Merge branch 'main' into develop
francescarpi Mar 3, 2026
9e935b3
feat: add new design tokens and toogle mode (#111)
itziarZG Mar 6, 2026
47ea970
Correcciones del theme y colores de los tiers aplicados (#112)
francescarpi Mar 8, 2026
9952f1d
Add Nagarro sponsor (#113)
francescarpi Mar 10, 2026
a1e273a
feat(sponsors): Add GISCE as a new bronze sponsor (#114)
dukebody Mar 11, 2026
9bd5401
chore(sponsors): Añadir Perk como main sponsor. (#115)
dukebody Mar 11, 2026
6684bbb
docs: add/update AGENTS.md with detailed guidelines for build, style,…
francescarpi Mar 12, 2026
e81b972
feat: add figma buttons (#121)
itziarZG Mar 13, 2026
66f81fd
Delete light mode (#122)
itziarZG Mar 17, 2026
33af271
feat(sponsors): redesign sponsors section with elegant glassmorphism …
francescarpi Mar 17, 2026
608e023
feat: delete menu options without pages (#126)
itziarZG Mar 17, 2026
011584a
fix: fix red letters contrast and increase base font size (#127)
itziarZG Mar 17, 2026
84aa0ce
feat: code conduct page (#123)
itziarZG Mar 17, 2026
3cd863a
feat: add analytics and seo (#128)
itziarZG Mar 17, 2026
1840ec4
feat: where and accomodation pages (#129)
itziarZG Mar 17, 2026
c1f6b5d
Feat add footer (#102)
itziarZG Mar 17, 2026
dfa5291
fix: fix code conduct dark mode (#133)
itziarZG Mar 18, 2026
d343115
feat: change header logo (#134)
itziarZG Mar 18, 2026
4340ec6
Feat: added a11y agents (#120)
itziarZG Mar 19, 2026
b286ea7
feat: replace UTF-8 emoji icons with unified Lucide icon system (#135)
francescarpi Mar 20, 2026
5af7147
Merge branch 'main' into develop
francescarpi Mar 21, 2026
8719adb
chore: rename build workflow to check and add pre-commit checks (#143)
francescarpi Mar 22, 2026
df2b0a8
chore: inject PUBLIC_GA_ID secret into build step (#144)
francescarpi Mar 22, 2026
54034ad
Merge branch 'main' into develop
francescarpi Mar 23, 2026
262474f
feat(accommodation): Actualizar página de alojamiento. (#149)
dukebody Mar 26, 2026
a325228
fix: some responsive adjustments (#151)
francescarpi Mar 31, 2026
49cf711
Quita código innecesario del theme (#152)
francescarpi Mar 31, 2026
f193d22
Corrige problema al detectar la opción de navegación activa cuando se…
francescarpi Mar 31, 2026
7e74d52
Quita caja repetida en página de sponsors y mejora los paddings (#156)
francescarpi Apr 1, 2026
8297aa3
Seccion CFP en la home (#157)
francescarpi Apr 1, 2026
b042a31
fix: fix anchor accessibility (#161)
itziarZG Apr 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/build.yml → .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ on:
branches: [main, develop]

jobs:
build:
check:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -22,5 +23,11 @@ jobs:
- name: Install dependencies
run: pnpm install

- name: Astro check
run: pnpm astro check

- name: Prettier check
run: pnpm exec prettier --check src

- name: Build
run: pnpm build
2 changes: 2 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ jobs:

- name: Build with Astro
run: pnpm run build
env:
PUBLIC_GA_ID: ${{ secrets.PUBLIC_GA_ID }}

- name: Upload artifact
if: github.event_name != 'pull_request'
Expand Down
34 changes: 33 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,30 +162,59 @@ Agents (like Copilot) must adhere to these coding standards to ensure consistenc
All new UI components and pages must be built with accessibility in mind from the start. Agents must prioritize the following core principles:

### 1. Semantic HTML & Structure

- **Use HTML5 elements:** Prioritize `<header>`, `<nav>`, `<main>`, `<section>`, `<article>`, and `<footer>` over generic `<div>`s. Screen readers depend on this semantics.
- **Heading hierarchy:** Always use headings in logical order (`<h1>` → `<h2>` → `<h3>`) without skipping levels.
- **Actions vs Navigation:** Use `<button>` for actions and `<a>` solely for links/navigation. Avoid using `<div>` or `<span>` with `onClick` handlers.

### 2. Text Alternatives (Images & Icons)

- **Image `alt` tags:** All `<img>` elements must have meaningful `alt` text. Describe the image's function or content (e.g., `alt="Persona usando la app en un celular"` not `alt="imagen1"`). If an image is purely decorative, strictly use `alt=""`.
- **SVGs and ARIA:** Ensure decorative SVGs have `aria-hidden="true"`. Interactive SVGs must have an `aria-label` or `<title>`. Provide `aria-` attributes (`aria-expanded`, `aria-describedby`) for dynamic elements where visual context isn't enough.

### 3. Color and Contrast

- **Contrast Ratios:** Ensure all text has sufficient contrast against its background. Avoid light grey text on white backgrounds.
- **Do not rely on color alone:** Always provide an additional visual indicator alongside color (e.g., rather than saying "Fields in red are required", say "Fields marked with * are required").
- **Do not rely on color alone:** Always provide an additional visual indicator alongside color (e.g., rather than saying "Fields in red are required", say "Fields marked with \* are required").

### 4. Keyboard Navigation

- **Tab navigation:** All interactive elements must be fully functional using only the keyboard (`Tab` and `Enter`/`Space`).
- **Visible Focus:** Ensure a clear, visible focus state for all focusable elements. Never use `outline: none;` without providing a custom visible focus ring (e.g., using Tailwind's `focus-visible:ring`).

### 5. External Links & New Tab Notifications

- **New tab links:** All links with `target="_blank"` must include `aria-label` that informs users the link opens in a new tab.
- **Use centralized translations:** Use `menuTexts[lang].new_tab` for consistent translations across all languages:
- Spanish: `(se abre en nueva pestaña)`
- English: `(opens in new tab)`
- Catalan: `(s'obre en una pestanya nova)`
- **Implementation example:**
```astro
<a
href="https://example.com"
target="_blank"
rel="noopener noreferrer"
aria-label={`${linkText} ${menuTexts[lang].new_tab}`}
>
{linkText}
</a>
```
- **Required imports:** Always import `menuTexts` when adding external links:
```astro
import {menuTexts} from '@/i18n/menu' const menuT = menuTexts[lang as keyof typeof menuTexts]
```

### Agent Enforcement

- When generating or modifying components, agents **must** proactively apply these accessibility standards without needing explicit prompting from the user.

## 5. SEO and Page Creation Guidelines

Agents must ensure all new pages are optimized for search engines and follow the project's internationalization (i18n) structure.

### Multi-language Pages

- All new pages must be placed in `src/pages/[lang]/`.
- Use `getStaticPaths()` to support all configured locales (`es`, `en`, `ca`).
- Example structure:
Expand All @@ -196,17 +225,20 @@ Agents must ensure all new pages are optimized for search engines and follow the
```

### Layout and Metadata

- Every page **must** use the `Layout` component from `src/layouts/Layout.astro`.
- Pass a unique and descriptive `title` and `description` (150-160 characters) to the `Layout` component.
- The `Layout` component automatically handles canonical URLs, social media tags (OG/Twitter), and `hreflang` tags.

### Semantic HTML and Accessibility

- **H1 Tags**: Use exactly one `<h1>` per page.
- **Headings**: Maintain a logical hierarchy (`h2`, `h3`, etc.).
- **Images**: All `<img>` tags must include a descriptive `alt` attribute.
- **Links**: Use descriptive text for links. Avoid generic phrases like "click here".

### Analytics and Monitoring

- Use the `PUBLIC_GA_ID` environment variable for Google Analytics.
- Do not hardcode tracking IDs.

Expand Down
165 changes: 114 additions & 51 deletions src/components/AccommodationPage.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
import { accommodationTexts } from '../i18n/accommodation'
import { menuTexts } from '../i18n/menu'
import StatusIcon from './icons/StatusIcon.astro'

interface Props {
Expand All @@ -8,6 +9,25 @@ interface Props {

const { lang } = Astro.props
const t = accommodationTexts[lang as keyof typeof accommodationTexts]
const menuT = menuTexts[lang as keyof typeof menuTexts]
type AreaSlug =
| 'eixampleEsquerra'
| 'elRaval'
| 'santAntoni'
| 'ciutatVella'
| 'sants'
| 'poblenou22'
| 'elClot'
| 'lesCorts'
| 'eixampleDreta'
| 'collblanc'

const walkingAreas: AreaSlug[] = ['eixampleEsquerra', 'elRaval', 'santAntoni', 'ciutatVella']
const directMetroAreas: AreaSlug[] = ['sants', 'poblenou22', 'elClot']
const transferAreas: AreaSlug[] = ['lesCorts', 'eixampleDreta', 'collblanc']

const areaName = (slug: AreaSlug) => t[`accommodation.areas.${slug}.name` as keyof typeof t]
const areaDesc = (slug: AreaSlug) => t[`accommodation.areas.${slug}.desc` as keyof typeof t]
---

<div class="accommodation-container pb-20">
Expand All @@ -20,33 +40,91 @@ const t = accommodationTexts[lang as keyof typeof accommodationTexts]
</p>
</section>

<section
class="max-w-4xl bg-white/5 backdrop-blur-md p-8 md:p-12 rounded-3xl border border-white/10 mb-16 shadow-2xl"
>
<p class="text-lg text-pycon-gray-25 leading-relaxed">
{t['accommodation.intro']}
</p>
<!-- Areas -->
<section class="mb-24" aria-labelledby="accommodation-areas-heading">
<div class="flex items-center gap-4 mb-12">
<div class="h-px bg-white/20 flex-1" aria-hidden="true"></div>
<h2 id="accommodation-areas-heading" class="text-3xl font-bold text-white px-4">
{t['accommodation.areas.title']}
</h2>
<div class="h-px bg-white/20 flex-1" aria-hidden="true"></div>
</div>
<div class="max-w-4xl space-y-4 mb-12 text-pycon-gray-25 leading-relaxed">
<p class="text-lg">{t['accommodation.areas.transportIntro.p1']}</p>
<p class="text-lg">{t['accommodation.areas.transportIntro.p2']}</p>
<p class="text-lg">{t['accommodation.areas.transportIntro.p3']}</p>
</div>

<div class="space-y-16">
<div>
<h3 class="text-2xl font-bold text-white mb-6">{t['accommodation.areas.category.walking']}</h3>
<ul class="grid sm:grid-cols-2 lg:grid-cols-3 gap-8 list-none m-0 p-0">
{
walkingAreas.map((slug) => (
<li class="bg-pycon-black/40 p-8 rounded-2xl border border-white/5 hover:border-pycon-orange/50 transition-all motion-safe:hover:-translate-y-2">
<h4 class="text-xl font-bold text-pycon-orange mb-4 italic">{areaName(slug)}</h4>
<p class="text-pycon-gray-25 text-sm leading-relaxed">{areaDesc(slug)}</p>
</li>
))
}
</ul>
</div>
<div>
<h3 class="text-2xl font-bold text-white mb-6">{t['accommodation.areas.category.directMetro']}</h3>
<ul class="grid sm:grid-cols-2 lg:grid-cols-3 gap-8 list-none m-0 p-0">
{
directMetroAreas.map((slug) => (
<li class="bg-pycon-black/40 p-8 rounded-2xl border border-white/5 hover:border-pycon-orange/50 transition-all motion-safe:hover:-translate-y-2">
<h4 class="text-xl font-bold text-pycon-orange mb-4 italic">{areaName(slug)}</h4>
<p class="text-pycon-gray-25 text-sm leading-relaxed">{areaDesc(slug)}</p>
</li>
))
}
</ul>
</div>
<div>
<h3 class="text-2xl font-bold text-white mb-6">{t['accommodation.areas.category.transferMetro']}</h3>
<ul class="grid sm:grid-cols-2 lg:grid-cols-3 gap-8 list-none m-0 p-0">
{
transferAreas.map((slug) => (
<li class="bg-pycon-black/40 p-8 rounded-2xl border border-white/5 hover:border-pycon-orange/50 transition-all motion-safe:hover:-translate-y-2">
<h4 class="text-xl font-bold text-pycon-orange mb-4 italic">{areaName(slug)}</h4>
<p class="text-pycon-gray-25 text-sm leading-relaxed">{areaDesc(slug)}</p>
</li>
))
}
</ul>
</div>
</div>
</section>

<!-- Hotels Section -->
<section class="mb-24">
<section class="mb-24" aria-labelledby="accommodation-hotels-heading">
<div class="flex items-center gap-4 mb-12">
<div class="h-px bg-white/20 flex-1"></div>
<h2 class="text-3xl font-bold text-white px-4">{t['accommodation.hotels.title']}</h2>
<div class="h-px bg-white/20 flex-1"></div>
<div class="h-px bg-white/20 flex-1" aria-hidden="true"></div>
<h2 id="accommodation-hotels-heading" class="text-3xl font-bold text-white px-4">
{t['accommodation.hotels.title']}
</h2>
<div class="h-px bg-white/20 flex-1" aria-hidden="true"></div>
</div>

<div
class="bg-pycon-gray-100/50 p-12 rounded-3xl border border-pycon-yellow/30 text-center relative overflow-hidden group"
>
<!-- Shine effect -->
<div
class="absolute -top-full -left-full w-[300%] h-[300%] bg-linear-to-br from-transparent via-pycon-yellow/5 to-transparent rotate-45 transition-transform duration-1000 group-hover:translate-x-[33%] group-hover:translate-y-[33%] pointer-events-none"
class="absolute -top-full -left-full w-[300%] h-[300%] bg-linear-to-br from-transparent via-pycon-yellow/5 to-transparent rotate-45 transition-transform duration-1000 motion-safe:group-hover:translate-x-[33%] motion-safe:group-hover:translate-y-[33%] pointer-events-none"
aria-hidden="true"
>
</div>

<div class="relative z-10">
<StatusIcon type="construction" size="xl" role="img" aria-label="Construyendo" />
<StatusIcon
type="construction"
size="xl"
role="img"
aria-label={t['accommodation.a11y.constructionIcon']}
/>
<h3 class="text-2xl font-bold text-pycon-yellow mb-4">{t['accommodation.hotels.subtitle']}</h3>
<p class="text-xl text-white font-medium max-w-2xl mx-auto italic opacity-80">
{t['accommodation.hotels.disclaimer']}
Expand All @@ -55,59 +133,37 @@ const t = accommodationTexts[lang as keyof typeof accommodationTexts]
</div>
</section>

<!-- Areas -->
<section class="mb-24">
<h2 class="text-3xl font-bold text-white mb-12 flex items-center gap-3">
<span class="w-2 h-8 bg-pycon-red rounded-full"></span>
{t['accommodation.areas.title']}
</h2>
<div class="grid md:grid-cols-3 gap-8">
<div
class="bg-pycon-black/40 p-8 rounded-2xl border border-white/5 hover:border-pycon-orange/50 transition-all hover:-translate-y-2"
>
<h3 class="text-xl font-bold text-pycon-orange mb-4 italic">
{t['accommodation.areas.eixample.name']}
</h3>
<p class="text-pycon-gray-25 text-sm leading-relaxed">{t['accommodation.areas.eixample.desc']}</p>
</div>
<div
class="bg-pycon-black/40 p-8 rounded-2xl border border-white/5 hover:border-pycon-orange/50 transition-all hover:-translate-y-2 text-center"
>
<h3 class="text-xl font-bold text-pycon-orange mb-4 italic">
{t['accommodation.areas.gothic.name']}
</h3>
<p class="text-pycon-gray-25 text-sm leading-relaxed">{t['accommodation.areas.gothic.desc']}</p>
</div>
<div
class="bg-pycon-black/40 p-8 rounded-2xl border border-white/5 hover:border-pycon-orange/50 transition-all hover:-translate-y-2 text-right"
>
<h3 class="text-xl font-bold text-pycon-orange mb-4 italic">
{t['accommodation.areas.poblenou.name']}
</h3>
<p class="text-pycon-gray-25 text-sm leading-relaxed">{t['accommodation.areas.poblenou.desc']}</p>
</div>
</div>
</section>

<!-- Apartments -->
<section
class="bg-linear-to-r from-pycon-red/20 to-pycon-orange/20 p-8 md:p-16 rounded-3xl border border-pycon-red/30"
aria-labelledby="accommodation-apartments-heading"
>
<div class="flex flex-col md:flex-row gap-12 items-center">
<div class="flex-1">
<h2 class="text-3xl font-bold text-white mb-6 uppercase tracking-tight">
<h2
id="accommodation-apartments-heading"
class="text-3xl font-bold text-white mb-6 uppercase tracking-tight"
>
{t['accommodation.apartments.title']}
</h2>
<p class="text-pycon-gray-25 text-lg mb-8 leading-relaxed">
{t['accommodation.apartments.text']}
</p>
<a
href="https://meet.barcelona/es/alojarse-en-barcelona"
href={t['accommodation.apartments.linkUrl']}
target="_blank"
class="inline-flex items-center gap-2 bg-white text-pycon-red font-bold px-6 py-3 rounded-lg hover:scale-105 transition-transform no-underline"
rel="noopener noreferrer"
aria-label={`${t['accommodation.apartments.link']} ${menuT.new_tab}`}
class="inline-flex items-center gap-2 bg-white text-pycon-red font-bold px-6 py-3 rounded-lg motion-safe:hover:scale-105 transition-transform no-underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-pycon-yellow"
>
{t['accommodation.apartments.link']}
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
<span class="sr-only"> ({t['accommodation.a11y.linkOpensNewTab']})</span>
<svg
class="w-5 h-5 shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
><path
stroke-linecap="round"
stroke-linejoin="round"
Expand All @@ -118,8 +174,9 @@ const t = accommodationTexts[lang as keyof typeof accommodationTexts]
</div>
<div
class="w-48 h-48 bg-white/10 rounded-full flex items-center justify-center border border-white/20 shadow-2xl backdrop-blur-xl shrink-0"
aria-hidden="true"
>
<StatusIcon type="city" size="xl" role="img" aria-label="Ubicación" />
<StatusIcon type="city" size="xl" />
</div>
</div>
</section>
Expand All @@ -140,4 +197,10 @@ const t = accommodationTexts[lang as keyof typeof accommodationTexts]
transform: translateY(0);
}
}

@media (prefers-reduced-motion: reduce) {
.accommodation-container {
animation: none;
}
}
</style>
2 changes: 1 addition & 1 deletion src/components/Button.astro
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const sizes = {
}

const baseClasses =
"inline-flex items-center justify-center font-['Outfit'] font-medium rounded-lg text-center transition-colors duration-200 cursor-pointer"
'inline-flex items-center justify-center font-medium rounded-lg text-center transition-colors duration-200 cursor-pointer'

const Element = href ? 'a' : 'button'
---
Expand Down
5 changes: 4 additions & 1 deletion src/components/LocationPage.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
---
import { locationTexts } from '../i18n/location'
import { menuTexts } from '../i18n/menu'

interface Props {
lang: string
}

const { lang } = Astro.props
const t = locationTexts[lang as keyof typeof locationTexts]
const menuT = menuTexts[lang as keyof typeof menuTexts]

const transportIcons = {
metro: 'M12 2L4.5 20.29L5.21 21L12 18L18.79 21L19.5 20.29L12 2Z',
Expand All @@ -19,7 +21,7 @@ const transportIcons = {

<div class="location-container pb-20">
<!-- Hero Section -->
<section class="relative h-[60vh] min-h-[400px] w-full overflow-hidden rounded-3xl mb-12 shadow-2xl">
<section class="relative h-[40vh] min-h-10 w-full overflow-hidden rounded-3xl mb-12 shadow-2xl">
<img
src="/images/Barcelona/seuUB.jpg"
alt={t['location.hero.title']}
Expand Down Expand Up @@ -129,6 +131,7 @@ const transportIcons = {
href="https://maps.app.goo.gl/bMt7iLyNT2N2Et786"
target="_blank"
rel="noopener noreferrer"
aria-label={`${t['location.map.link']} ${menuT.new_tab}`}
class="inline-block w-full text-center bg-white text-pycon-red font-bold py-3 rounded-lg hover:bg-pycon-gray-25 transition-colors shadow-lg"
>
{t['location.map.link']}
Expand Down
Loading
Loading