Skip to content

Commit b6c4767

Browse files
committed
feat: redesign meetup detail page with card layout and carousel
- Replaces plain layout with breadcrumb nav, hero section, and two-column card grid (host + speakers left, talk carousel right) - Adds vanilla JS carousel using scroll-snap + IntersectionObserver with prev/next buttons and slide counter - Updates meetup list to use Spanish month abbreviations and adds year anchor IDs for breadcrumb deep-links - Fixes accessible breadcrumb separators and adds speaker.photo fallback in carousel slide avatars
1 parent 0d416de commit b6c4767

7 files changed

Lines changed: 1131 additions & 84 deletions

File tree

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
3232
gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
3333

3434
gem "webrick", "~> 1.8"
35+

Gemfile.lock

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ GEM
99
eventmachine (>= 0.12.9)
1010
http_parser.rb (~> 0)
1111
eventmachine (1.2.7)
12-
ffi (1.16.3)
12+
ffi (1.17.4-arm64-darwin)
13+
ffi (1.17.4-x86_64-darwin)
14+
ffi (1.17.4-x86_64-linux-gnu)
1315
forwardable-extended (2.6.0)
1416
google-protobuf (4.26.1-arm64-darwin)
1517
rake (>= 13)
@@ -83,6 +85,7 @@ GEM
8385
PLATFORMS
8486
arm64-darwin-22
8587
arm64-darwin-23
88+
arm64-darwin-24
8689
x86_64-darwin-21
8790
x86_64-linux
8891

_includes/meetups.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ <h1>EDICIONES PASADAS</h1>
44
<p>Conocé el contenido de las ediciones pasadas.</p>
55
</header>
66

7+
{% assign months_es = "ENE,FEB,MAR,ABR,MAY,JUN,JUL,AGO,SEP,OCT,NOV,DIC" | split: ',' %}
78
{% assign meetups_sorted = site.meetups | sort: "date" | reverse %}
89

910
{% assign meetups_by_year = meetups_sorted
@@ -12,14 +13,14 @@ <h1>EDICIONES PASADAS</h1>
1213
| reverse %}
1314

1415
{% for year in meetups_by_year | reverse %}
15-
<h2 class="meetup-event__date_year">{{ year.name }}</h2>
16+
<h2 class="meetup-event__date_year" id="meetups-{{ year.name }}">{{ year.name }}</h2>
1617

1718
<ul>
1819
{% for meetup in year.items %}
1920
<li class="meetup-event">
2021
<a href="{{ meetup.url }}" class="meetup-event__link">
2122
<span class="meetup-event__date">
22-
{{ meetup.date | date_to_string }}
23+
{% assign m_idx = meetup.date | date: '%m' | minus: 1 %}{{ meetup.date | date: "%d" }} {{ months_es[m_idx] }}
2324
</span>
2425
<span class="meetup-event__company">
2526
{{ meetup.title }}

_layouts/meetup.html

Lines changed: 249 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,75 +3,273 @@
33
{% include head.html %}
44

55
<body>
6+
{% assign host = site.data.companies[page.host] %}
7+
{% assign host_name = host.name | default: page.host %}
8+
{% assign host_domain = host.url | remove: 'https://' | remove: 'http://' | remove: 'www.' | split: '/' | first %}
9+
{% assign year = page.date | date: '%Y' %}
10+
{% assign months_es = "ENE,FEB,MAR,ABR,MAY,JUN,JUL,AGO,SEP,OCT,NOV,DIC" | split: ',' %}
11+
{% assign month_idx = page.date | date: '%m' | minus: 1 %}
12+
{% assign month_name = months_es[month_idx] %}
13+
614
{% include nav.html %}
715

816
<main>
917
<article id="view-meetup">
10-
<section>
11-
<h2>Host</h2>
12-
<a href="{{ site.data.companies[page.host].url }}">{{ site.data.companies[page.host].name }}</a>
13-
</section>
18+
<nav class="view-meetup__breadcrumb" aria-label="breadcrumb">
19+
<div class="breadcrumb__inner">
20+
<ol class="breadcrumb__trail">
21+
<li><a href="/">RUBY.UY</a></li>
22+
<li><span aria-hidden="true">/</span></li>
23+
<li><a href="/#meetups">MEETUPS</a></li>
24+
<li><span aria-hidden="true">/</span></li>
25+
<li><a href="/#meetups-{{ year }}">{{ year }}</a></li>
26+
<li><span aria-hidden="true">/</span></li>
27+
<li class="breadcrumb__current">{{ host_name | upcase }}</li>
28+
</ol>
29+
</div>
30+
</nav>
1431

15-
<section>
16-
<h2>Speakers</h2>
17-
<ul>
18-
{% for talk in page.talks %}
19-
{% for speaker in talk.speakers %}
20-
<li>
21-
{% if site.data.people[speaker].github %}
22-
<a href="https://github.com/{{ site.data.people[speaker].github }}">
23-
<img src="https://github.com/{{ site.data.people[speaker].github }}.png?size=30" alt="{{ site.data.people[speaker].name }}" style="border-radius: 50%; width: 30px"/>{{ site.data.people[speaker].name }}
24-
</a>
25-
{% else %}
26-
{{ site.data.people[speaker].name }}
32+
<section class="view-meetup__hero">
33+
<div class="hero__inner">
34+
<div class="hero__left">
35+
<div class="hero__heading">
36+
<div class="date-badge" aria-label="{{ page.date | date: '%Y-%m-%d' }}">
37+
<span class="date-badge__day">{{ page.date | date: '%d' }}</span>
38+
<span class="date-badge__month">{{ month_name }}</span>
39+
<span class="date-badge__year">{{ year }}</span>
40+
</div>
41+
<div class="hero__title-block">
42+
<span class="hero__pill">MEETUP</span>
43+
<h1 class="hero__title">
44+
<span class="hero__title-line">RUBY MEETUP</span>
45+
<span class="hero__title-line">@ {{ host_name | upcase }}</span>
46+
</h1>
47+
</div>
48+
</div>
49+
50+
{% if page.description %}
51+
<p class="hero__description">{{ page.description }}</p>
2752
{% endif %}
28-
</li>
29-
{% endfor %}
30-
{% endfor %}
31-
</ul>
53+
</div>
54+
55+
<div class="hero__right" aria-hidden="true">
56+
<div class="hero__illustration">
57+
<svg viewBox="0 0 320 220" xmlns="http://www.w3.org/2000/svg">
58+
<g fill="#3967D1" opacity="0.16">
59+
{% for row in (0..7) %}
60+
{% for col in (0..8) %}
61+
<circle cx="{{ col | times: 20 | plus: 145 }}" cy="{{ row | times: 20 | plus: 15 }}" r="2"/>
62+
{% endfor %}
63+
{% endfor %}
64+
</g>
65+
<g transform="translate(160, 110)">
66+
<polygon points="-140,-20 -90,-90 90,-90 140,-20 0,90"
67+
fill="#E63946"
68+
stroke="#9B1F2D"
69+
stroke-width="1.6"
70+
stroke-linejoin="round"/>
71+
<polygon points="-80,-20 -45,-90 45,-90 80,-20"
72+
fill="#FF6B7A"
73+
fill-opacity="0.55"/>
74+
<polygon points="-140,-20 140,-20 0,90"
75+
fill="#9B1F2D"
76+
fill-opacity="0.2"/>
77+
<g stroke="#9B1F2D" stroke-width="0.9" fill="none" opacity="0.42">
78+
<line x1="-80" y1="-20" x2="-45" y2="-90"/>
79+
<line x1="80" y1="-20" x2="45" y2="-90"/>
80+
<line x1="-80" y1="-20" x2="0" y2="90"/>
81+
<line x1="80" y1="-20" x2="0" y2="90"/>
82+
<line x1="-140" y1="-20" x2="140" y2="-20"/>
83+
</g>
84+
</g>
85+
</svg>
86+
</div>
87+
</div>
88+
</div>
3289
</section>
3390

34-
<section>
35-
<h2>Charlas</h2>
91+
<section class="view-meetup__grid-section">
92+
<div class="grid-section__inner">
93+
<div class="view-meetup__grid">
94+
<div class="view-meetup__col view-meetup__col--left">
95+
<section class="card host-card">
96+
<span class="card__label card__label--host">HOST</span>
97+
<div class="host-card__body">
98+
{% if host.image %}
99+
<div class="host-card__logo-tile">
100+
<img class="host-card__logo" src="{{ host.image }}" alt="{{ host_name }}">
101+
</div>
102+
{% endif %}
103+
<div class="host-card__info">
104+
<h2 class="host-card__name">{{ host_name | upcase }}</h2>
105+
{% if host.description %}
106+
<p class="host-card__description">{{ host.description }}</p>
107+
{% endif %}
108+
{% if host.url %}
109+
<a class="host-card__link" href="{{ host.url }}" target="_blank" rel="noopener">
110+
{{ host_domain }} <span class="host-card__link-icon" aria-hidden="true"></span>
111+
</a>
112+
{% endif %}
113+
</div>
114+
</div>
115+
</section>
36116

37-
{% for talk in page.talks %}
38-
<div class="talk">
39-
{% assign speakers = "" | split: ',' %}
117+
{% assign seen_speakers = "" | split: "," %}
118+
{% assign any_speakers = false %}
119+
{% for talk in page.talks %}
120+
{% for speaker_key in talk.speakers %}
121+
{% unless seen_speakers contains speaker_key %}
122+
{% assign seen_speakers = seen_speakers | push: speaker_key %}
123+
{% assign any_speakers = true %}
124+
{% endunless %}
125+
{% endfor %}
126+
{% endfor %}
40127

41-
{% for speaker in talk.speakers %}
42-
{% assign speakers = speakers | push: site.data.people[speaker].name %}
43-
{% endfor %}
128+
{% if any_speakers %}
129+
<section class="card speakers-card">
130+
<span class="card__label card__label--speakers">SPEAKERS</span>
131+
<ul class="speakers-list">
132+
{% for speaker_key in seen_speakers %}
133+
{% assign speaker = site.data.people[speaker_key] %}
134+
<li class="speaker">
135+
{% if speaker.photo %}
136+
<img class="speaker__avatar" src="{{ speaker.photo }}" alt="{{ speaker.name }}">
137+
{% elsif speaker.github %}
138+
<img class="speaker__avatar" src="https://github.com/{{ speaker.github }}.png?size=200" alt="{{ speaker.name }}">
139+
{% else %}
140+
{% assign speaker_name_parts = speaker.name | split: " " %}
141+
{% assign speaker_initial_first = speaker_name_parts[0] | slice: 0 | upcase %}
142+
{% if speaker_name_parts.size > 1 %}
143+
{% assign speaker_last_part = speaker_name_parts | last %}
144+
{% assign speaker_initial_last = speaker_last_part | slice: 0 | upcase %}
145+
{% assign speaker_initials = speaker_initial_first | append: speaker_initial_last %}
146+
{% else %}
147+
{% assign speaker_initials = speaker_initial_first %}
148+
{% endif %}
149+
<span class="speaker__avatar speaker__avatar--placeholder" aria-hidden="true">{{ speaker_initials }}</span>
150+
{% endif %}
151+
<div class="speaker__info">
152+
<h3 class="speaker__name">{{ speaker.name | upcase }}</h3>
153+
{% if speaker.role %}
154+
<p class="speaker__role">{{ speaker.role }}</p>
155+
{% endif %}
156+
<ul class="speaker__socials">
157+
{% if speaker.github %}
158+
<li>
159+
<a href="https://github.com/{{ speaker.github }}" target="_blank" rel="noopener">
160+
GitHub <span class="speaker__socials-arrow" aria-hidden="true"></span>
161+
</a>
162+
</li>
163+
{% endif %}
164+
{% if speaker.twitter %}
165+
<li>
166+
<a href="https://twitter.com/{{ speaker.twitter }}" target="_blank" rel="noopener">
167+
Twitter <span class="speaker__socials-arrow" aria-hidden="true"></span>
168+
</a>
169+
</li>
170+
{% endif %}
171+
</ul>
172+
</div>
173+
</li>
174+
{% endfor %}
175+
</ul>
176+
</section>
177+
{% endif %}
178+
</div>
44179

45-
<h3 id="{{ talk.title | slugify }}">
46-
<a href="#{{ talk.title | slugify }}">
47-
<span class="talk-title">{{ talk.title }}</span>
48-
</a>
49-
<p>{{ speakers | join: ", " }}</p>
50-
</h3>
180+
<div class="view-meetup__col view-meetup__col--right">
181+
<section class="card charla-destacada" id="charla-destacada">
182+
{% if page.talks.size > 0 %}
183+
<header class="charla-destacada__header">
184+
<span class="charla-destacada__counter" data-counter aria-live="polite">
185+
01 / {% if page.talks.size < 10 %}0{% endif %}{{ page.talks.size }}
186+
</span>
187+
</header>
188+
{% endif %}
51189

52-
<p>{{ talk.description }}<p>
190+
<div class="carousel" data-carousel>
191+
<ol class="carousel__track" data-track>
192+
{% for talk in page.talks %}
193+
<li class="slide" data-slide>
194+
<h3 class="slide__title" id="{{ talk.title | slugify }}">
195+
<a href="#{{ talk.title | slugify }}">
196+
{{ talk.title | upcase }}
197+
</a>
198+
</h3>
53199

54-
{% if talk.recording %}
55-
<div class="keep-aspect-ratio">
56-
<iframe src="{{ talk.recording }}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen>
57-
</iframe>
58-
</div>
59-
{% endif %}
200+
<p class="slide__speakers">
201+
{% for speaker_key in talk.speakers %}
202+
{% assign speaker = site.data.people[speaker_key] %}
203+
{% if speaker.photo %}
204+
<img class="slide__speaker-avatar" src="{{ speaker.photo }}" alt="{{ speaker.name }}">
205+
{% elsif speaker.github %}
206+
<img class="slide__speaker-avatar" src="https://github.com/{{ speaker.github }}.png?size=60" alt="{{ speaker.name }}">
207+
{% else %}
208+
<span class="slide__speaker-avatar slide__speaker-avatar--placeholder" aria-hidden="true"></span>
209+
{% endif %}
210+
<span>{{ speaker.name }}</span>{% unless forloop.last %}<span class="slide__speaker-sep">, </span>{% endunless %}
211+
{% endfor %}
212+
</p>
60213

61-
{% if talk.slides %}
62-
<div class="keep-aspect-ratio">
63-
<iframe src="{{ talk.slides }}" frameborder="0" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true">
64-
</iframe>
65-
</div>
66-
{% endif %}
214+
{% if talk.recording %}
215+
<div class="keep-aspect-ratio">
216+
<iframe src="{{ talk.recording }}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen loading="lazy"></iframe>
217+
</div>
218+
{% endif %}
67219

220+
{% if talk.slides %}
221+
<div class="keep-aspect-ratio">
222+
<iframe src="{{ talk.slides }}" frameborder="0" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" loading="lazy"></iframe>
223+
</div>
224+
{% endif %}
225+
226+
{% if talk.topics or talk.duration %}
227+
<footer class="slide__footer">
228+
{% if talk.topics %}
229+
<div class="slide__meta slide__meta--tema">
230+
<span class="slide__meta-label">TEMA</span>
231+
<span class="slide__meta-value">{{ talk.topics | join: ', ' }}</span>
232+
</div>
233+
{% endif %}
234+
{% if talk.duration %}
235+
<div class="slide__meta slide__meta--duracion">
236+
<span class="slide__meta-label">DURACIÓN</span>
237+
<span class="slide__meta-value">{{ talk.duration }}</span>
238+
</div>
239+
{% endif %}
240+
</footer>
241+
{% endif %}
242+
</li>
243+
{% endfor %}
244+
</ol>
245+
246+
{% if page.talks.size > 1 %}
247+
<button class="carousel__btn carousel__btn--prev" type="button" data-prev aria-label="Charla anterior">&lsaquo;</button>
248+
<button class="carousel__btn carousel__btn--next" type="button" data-next aria-label="Siguiente charla">&rsaquo;</button>
249+
{% endif %}
250+
</div>
251+
</section>
252+
</div>
253+
</div>
68254
</div>
69-
{% endfor %}
70255
</section>
71-
<section> {{ content }} </section>
256+
257+
{% if content and content != "" %}
258+
<section class="view-meetup__content">{{ content }}</section>
259+
{% endif %}
260+
261+
<nav class="view-meetup__footer-bar">
262+
<div class="footer-bar__inner">
263+
<a href="/#meetups">&larr; VER MÁS EDICIONES PASADAS</a>
264+
</div>
265+
</nav>
72266
</article>
73267
</main>
74-
</body>
75268

76-
{% include footer.html %}
269+
{% if page.talks.size > 0 %}
270+
<script src="/assets/js/meetup_carousel.js" defer></script>
271+
{% endif %}
272+
273+
{% include footer.html %}
274+
</body>
77275
</html>

_sass/meetups.scss

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#meetups {
22
background-color: #F6EEEC;
3+
display: flex;
4+
flex-direction: column;
5+
min-height: 100vh;
6+
width: 100%;
37

48
header {
59
background-color: #3967D1;
@@ -36,10 +40,12 @@
3640

3741
.meetup-event__date_year {
3842
align-items: center;
43+
border-bottom: 2px solid #2851AE;
3944
display: flex;
40-
padding: 1.5rem 8rem;
4145
font-weight: bold;
4246
justify-content: space-between;
47+
margin: 2.75rem 0 0.875rem;
48+
padding: 1.25rem 8rem 0.625rem;
4349
}
4450

4551
ul {

0 commit comments

Comments
 (0)