Skip to content

Commit 44b57e2

Browse files
feat: add store page and libvirtualhid launch
Introduce a new Store section with product data, reusable product cards, storefront styling, and gallery behavior for the Virtual HID Driver listing. This also adds a launch blog post for libvirtualhid and Virtual HID Driver, wires the Store link into the navbar.
1 parent 8800b9e commit 44b57e2

8 files changed

Lines changed: 980 additions & 1 deletion

_config_theme.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ navbar-links:
1313
- GitHub: "https://github.com/sponsors/LizardByte"
1414
- Patreon: "https://patreon.com/LizardByte"
1515
- PayPal: "https://www.paypal.com/paypalme/ReenigneArcher"
16+
Store: "store"
1617
Resources:
1718
- Blog: "blog"
1819
- Community: "https://github.com/orgs/LizardByte/discussions"

_data/amazon_products.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@
230230
# Accessories
231231
- title: >-
232232
Elgato Stream Deck MK.2 –
233-
Studio Controller, 15 macro keys, trigger actions in apps and software like OBS, Twitch, YouTube and more,
233+
Studio Controller, 15 macro keys, trigger actions in apps and software like OBS, Twitch, YouTube and more,
234234
works with Mac and PC
235235
image: "https://m.media-amazon.com/images/I/61gtdFnK+UL._AC_SL1500_.jpg"
236236
link: "https://www.amazon.com/dp/B09738CV2G"

_data/store_products.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
- id: virtual-hid-driver
3+
name: Virtual HID Driver
4+
category: Windows software
5+
tagline: Modern virtual gamepads for Sunshine on Windows
6+
description: >-
7+
Unlock expanded virtual controller support in Sunshine with a user-mode driver built and maintained alongside
8+
the rest of the LizardByte input stack.
9+
cover:
10+
src: https://raw.githubusercontent.com/LizardByte/libvirtualhid/refs/heads/master/libvirtualhid-poster-1440x2160.png
11+
alt: A green outline of a game controller on a vertical dark green background
12+
aspect: portrait
13+
screenshots:
14+
- src: "https://raw.githubusercontent.com/LizardByte/libvirtualhid/refs/heads/master/docs/images/screenshots/\
15+
libvirtualhid-control-03.png"
16+
alt: libvirtualhid control displaying an active Xbox Series virtual controller
17+
caption: Inspect virtual controller input, battery state, and output reports.
18+
- src: "https://raw.githubusercontent.com/LizardByte/libvirtualhid/refs/heads/master/docs/images/screenshots/\
19+
libvirtualhid-control-04.png"
20+
alt: libvirtualhid control displaying a DualSense virtual controller
21+
caption: Use controller-specific features such as touchpad input and RGB output.
22+
- src: "https://raw.githubusercontent.com/LizardByte/libvirtualhid/refs/heads/master/docs/images/screenshots/\
23+
windows-driver-game-controllers-and-properties.png"
24+
alt: Windows Game Controllers and controller properties showing a Virtual HID Framework device
25+
caption: Virtual controllers appear in the familiar Windows Game Controllers panel.
26+
badges:
27+
- label: Windows
28+
icon: fa-brands fa-windows
29+
- label: Sunshine compatible
30+
icon: fa-solid fa-sun
31+
prices:
32+
- amount: $14.99
33+
label: per year
34+
- amount: $49.99
35+
label: lifetime
36+
price_note: One license can be activated on up to five machines.
37+
features:
38+
- Generic, Xbox One, Xbox Series, DualShock 4, DualSense, and Switch Pro-style profiles
39+
- Support for advanced controller features when the selected controller and game provide them
40+
- Ongoing driver maintenance and improvements alongside Sunshine and other LizardByte software
41+
details:
42+
label: Learn more
43+
url: https://app.lizardbyte.dev/libvirtualhid/virtual-hid-driver
44+
offers:
45+
- label: Purchase
46+
url: https://buy.polar.sh/polar_cl_zj6Io5NVukXfZSl97ULtFvImfI5L1jbL2cSnc0Y72Pt
47+
store: Polar
48+
external: true
49+
icon: fa-solid fa-cart-shopping

_includes/store-product-card.html

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
{% assign product = include.product %}
2+
<article class="store-product-card{% if include.featured %} store-product-card-featured{% endif %}">
3+
<div class="store-product-summary">
4+
<div class="store-product-media store-product-media-{{ product.cover.aspect | default: 'landscape' }}">
5+
<a href="{{ product.details.url }}" aria-label="Learn more about {{ product.name | escape }}">
6+
<img
7+
class="store-product-image crowdin-ignore"
8+
src="{{ product.cover.src }}"
9+
alt="{{ product.cover.alt | escape }}"
10+
loading="lazy"
11+
>
12+
</a>
13+
{% if product.badges %}
14+
<ul class="store-product-badges list-unstyled" aria-label="Product compatibility">
15+
{% for badge in product.badges %}
16+
<li class="store-product-badge">
17+
{% if badge.icon %}<i class="{{ badge.icon }}" aria-hidden="true"></i>{% endif %}
18+
{{ badge.label }}
19+
</li>
20+
{% endfor %}
21+
</ul>
22+
{% endif %}
23+
</div>
24+
25+
<div class="store-product-content">
26+
<div class="store-product-details">
27+
<div class="store-product-heading">
28+
<span class="store-product-category">{{ product.category }}</span>
29+
<h3 class="store-product-title">{{ product.name }}</h3>
30+
<p class="store-product-tagline">{{ product.tagline }}</p>
31+
</div>
32+
33+
<p class="store-product-description">{{ product.description }}</p>
34+
35+
{% if product.features %}
36+
<ul class="store-product-features">
37+
{% for feature in product.features %}
38+
<li>
39+
<i class="fa-solid fa-circle-check" aria-hidden="true"></i>
40+
<span>{{ feature }}</span>
41+
</li>
42+
{% endfor %}
43+
</ul>
44+
{% endif %}
45+
</div>
46+
47+
<div class="store-product-purchase">
48+
{% if product.prices %}
49+
<div class="store-product-prices" aria-label="Pricing options">
50+
{% for price in product.prices %}
51+
<div class="store-price-option">
52+
<span class="store-price-amount crowdin-ignore">{{ price.amount }}</span>
53+
<span class="store-price-label">{{ price.label }}</span>
54+
</div>
55+
{% endfor %}
56+
</div>
57+
{% endif %}
58+
59+
{% if product.price_note %}
60+
<p class="store-product-price-note">
61+
<i class="fa-solid fa-display" aria-hidden="true"></i>
62+
{{ product.price_note }}
63+
</p>
64+
{% endif %}
65+
66+
<div class="store-product-actions">
67+
{% for offer in product.offers %}
68+
<a
69+
class="btn btn-primary"
70+
href="{{ offer.url }}"
71+
{% if offer.external %}
72+
target="_blank"
73+
rel="noopener"
74+
{% endif %}
75+
>
76+
{% if offer.icon %}<i class="{{ offer.icon }}" aria-hidden="true"></i>{% endif %}
77+
{{ offer.label }}
78+
<span class="visually-hidden">
79+
for {{ product.name }}{% if offer.store %} from {{ offer.store }}{% endif %}
80+
</span>
81+
</a>
82+
{% endfor %}
83+
{% if product.details %}
84+
<a class="btn btn-outline-theme" href="{{ product.details.url }}">
85+
{{ product.details.label }}
86+
<i class="fa-solid fa-arrow-right" aria-hidden="true"></i>
87+
</a>
88+
{% endif %}
89+
</div>
90+
91+
{% for offer in product.offers %}
92+
{% if offer.store %}
93+
<p class="store-product-seller">
94+
<i class="fa-solid fa-lock" aria-hidden="true"></i>
95+
Checkout provided by {{ offer.store }}
96+
</p>
97+
{% endif %}
98+
{% endfor %}
99+
</div>
100+
</div>
101+
</div>
102+
103+
{% if product.screenshots %}
104+
<section class="store-product-gallery" aria-labelledby="{{ product.id }}-screenshots">
105+
<div class="store-product-gallery-heading">
106+
<span class="store-product-category">Gallery</span>
107+
<h4 id="{{ product.id }}-screenshots">Screenshots</h4>
108+
</div>
109+
<div class="store-screenshot-grid">
110+
{% for screenshot in product.screenshots %}
111+
<button
112+
class="store-screenshot-thumbnail"
113+
type="button"
114+
data-bs-toggle="modal"
115+
data-bs-target="#{{ product.id }}-gallery"
116+
data-store-slide="{{ forloop.index0 }}"
117+
aria-label="View screenshot {{ forloop.index }} of {{ product.screenshots.size }}: {{ screenshot.alt | escape }}"
118+
>
119+
<img
120+
class="crowdin-ignore"
121+
src="{{ screenshot.src }}"
122+
alt=""
123+
loading="lazy"
124+
>
125+
<span class="store-screenshot-expand" aria-hidden="true">
126+
<i class="fa-solid fa-expand"></i>
127+
</span>
128+
</button>
129+
{% endfor %}
130+
</div>
131+
</section>
132+
{% endif %}
133+
</article>
134+
135+
{% if product.screenshots %}
136+
<div
137+
class="modal fade store-gallery-modal"
138+
id="{{ product.id }}-gallery"
139+
tabindex="-1"
140+
aria-labelledby="{{ product.id }}-gallery-title"
141+
aria-hidden="true"
142+
>
143+
<div class="modal-dialog modal-xl modal-dialog-centered">
144+
<div class="modal-content">
145+
<div class="modal-header">
146+
<h2 class="modal-title fs-5" id="{{ product.id }}-gallery-title">
147+
{{ product.name }} screenshots
148+
</h2>
149+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
150+
</div>
151+
<div class="modal-body">
152+
<div
153+
class="carousel slide store-gallery-carousel"
154+
id="{{ product.id }}-carousel"
155+
data-bs-interval="false"
156+
data-bs-keyboard="true"
157+
tabindex="-1"
158+
aria-label="{{ product.name | escape }} screenshot viewer"
159+
>
160+
<div class="carousel-inner">
161+
{% for screenshot in product.screenshots %}
162+
<figure class="carousel-item{% if forloop.first %} active{% endif %}">
163+
<img
164+
class="store-gallery-image crowdin-ignore"
165+
src="{{ screenshot.src }}"
166+
alt="{{ screenshot.alt | escape }}"
167+
>
168+
{% if screenshot.caption %}
169+
<figcaption class="store-gallery-caption">{{ screenshot.caption }}</figcaption>
170+
{% endif %}
171+
</figure>
172+
{% endfor %}
173+
</div>
174+
175+
{% if product.screenshots.size > 1 %}
176+
<button
177+
class="carousel-control-prev"
178+
type="button"
179+
data-bs-target="#{{ product.id }}-carousel"
180+
data-bs-slide="prev"
181+
>
182+
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
183+
<span class="visually-hidden">Previous screenshot</span>
184+
</button>
185+
<button
186+
class="carousel-control-next"
187+
type="button"
188+
data-bs-target="#{{ product.id }}-carousel"
189+
data-bs-slide="next"
190+
>
191+
<span class="carousel-control-next-icon" aria-hidden="true"></span>
192+
<span class="visually-hidden">Next screenshot</span>
193+
</button>
194+
{% endif %}
195+
</div>
196+
</div>
197+
</div>
198+
</div>
199+
</div>
200+
{% endif %}

0 commit comments

Comments
 (0)