Skip to content

Commit 20b1210

Browse files
committed
feat(brand): Add example app and brand
1 parent fc66b21 commit 20b1210

File tree

2 files changed

+378
-0
lines changed

2 files changed

+378
-0
lines changed

examples/brand/_brand.yml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
meta:
2+
name:
3+
full: "Retro Arcade Brand"
4+
short: "RetroArc"
5+
link:
6+
home: https://retroarc.example.com
7+
mastodon: https://mastodon.social/@retroarc
8+
github: https://github.com/retroarc
9+
linkedin: https://linkedin.com/company/retroarc
10+
twitter: https://twitter.com/retroarc
11+
facebook: https://facebook.com/retroarc
12+
13+
# logo:
14+
# images:
15+
# icon-light: logos/retroarc-icon-light.png
16+
# icon-dark: logos/retroarc-icon-dark.png
17+
# wide-light: logos/retroarc-wide-light.png
18+
# wide-dark: logos/retroarc-wide-dark.png
19+
# tall-light: logos/retroarc-tall-light.png
20+
# tall-dark: logos/retroarc-tall-dark.png
21+
# small:
22+
# light: logos/retroarc-icon-light.png
23+
# dark: logos/retroarc-icon-dark.png
24+
# medium:
25+
# light: logos/retroarc-wide-light.png
26+
# dark: logos/retroarc-wide-dark.png
27+
# large:
28+
# light: logos/retroarc-tall-light.png
29+
# dark: logos/retroarc-tall-dark.png
30+
31+
color:
32+
palette:
33+
pink: "#E83E8C"
34+
blue: "#007BFF"
35+
cyan: "#17A2B8"
36+
teal: "#20C997"
37+
green: "#28A745"
38+
yellow: "#FFD700"
39+
orange: "#FF7F50"
40+
red: "#FF3333"
41+
purple: "#6F42C1"
42+
indigo: "#6610F2"
43+
black: "#1A1A1A"
44+
white: "#F8F8F8"
45+
foreground: black
46+
background: white
47+
primary: purple
48+
success: green
49+
info: cyan
50+
warning: yellow
51+
danger: orange
52+
light: white
53+
dark: black
54+
55+
typography:
56+
fonts:
57+
- family: "Quantico"
58+
source: google
59+
weight: [700]
60+
style: [normal]
61+
display: swap
62+
- family: "Monda"
63+
source: google
64+
weight: 400..700
65+
style: [normal, italic]
66+
display: swap
67+
- family: "Courier Prime"
68+
source: google
69+
weight: [400, 700]
70+
style: [normal, italic]
71+
display: swap
72+
base:
73+
family: "Monda"
74+
size: "1em"
75+
weight: 400
76+
line-height: 1.5
77+
headings:
78+
family: "Quantico"
79+
weight: 400
80+
line-height: 1.2
81+
style: normal
82+
monospace:
83+
family: "Courier Prime"
84+
size: "0.9em"
85+
weight: 400
86+
monospace-inline:
87+
family: "Courier Prime"
88+
size: "0.9em"
89+
weight: 400
90+
color: yellow
91+
background-color: "#1a1a1add"
92+
monospace-block:
93+
family: "Courier Prime"
94+
size: "0.9em"
95+
weight: 400
96+
color: green
97+
background-color: black
98+
line-height: 1.4
99+
link:
100+
weight: 400
101+
background-color: purple
102+
color: white
103+
decoration: "underline"
104+
105+
defaults:
106+
shiny:
107+
theme:
108+
navbar-bg: $brand-purple

examples/brand/app.py

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
import os
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
6+
from shiny import App, render, ui
7+
8+
# TODO: Move this into the test that runs this app
9+
os.environ["SHINY_BRAND_YML_RAISE_UNMAPPED"] = "true"
10+
11+
theme = ui.Theme.from_brand(__file__)
12+
13+
app_ui = ui.page_navbar(
14+
ui.nav_panel(
15+
"Input Output Demo",
16+
ui.page_sidebar(
17+
ui.sidebar(
18+
ui.input_slider("slider1", "Numeric Slider Input", 0, 11, 11),
19+
ui.input_numeric("numeric1", "Numeric Input Widget", 30),
20+
ui.input_date("date1", "Date Input Component", value="2024-01-01"),
21+
ui.input_switch("switch1", "Binary Switch Input", True),
22+
ui.input_radio_buttons(
23+
"radio1",
24+
"Radio Button Group",
25+
choices=["Option A", "Option B", "Option C", "Option D"],
26+
),
27+
ui.input_action_button("action1", "Action Button"),
28+
),
29+
ui.layout_column_wrap(
30+
ui.value_box(
31+
"Metric 1",
32+
"100",
33+
theme="primary",
34+
),
35+
ui.value_box(
36+
"Metric 2",
37+
"200",
38+
theme="secondary",
39+
),
40+
ui.value_box(
41+
"Metric 3",
42+
"300",
43+
theme="info",
44+
),
45+
),
46+
ui.card(
47+
ui.card_header("Plot Output"),
48+
ui.output_plot("plot1"),
49+
),
50+
ui.card(
51+
ui.card_header("Text Output"),
52+
ui.output_text_verbatim("out_text1"),
53+
),
54+
),
55+
),
56+
ui.nav_panel(
57+
"Widget Gallery",
58+
ui.layout_column_wrap(
59+
ui.card(
60+
ui.card_header("Button Variants"),
61+
ui.input_action_button("btn1", "Default"),
62+
ui.input_action_button("btn2", "Primary", class_="btn-primary"),
63+
ui.input_action_button("btn3", "Secondary", class_="btn-secondary"),
64+
ui.input_action_button("btn4", "Info", class_="btn-info"),
65+
ui.input_action_button("btn5", "Success", class_="btn-success"),
66+
ui.input_action_button("btn6", "Warning", class_="btn-warning"),
67+
ui.input_action_button("btn7", "Danger", class_="btn-danger"),
68+
),
69+
ui.card(
70+
ui.card_header("Radio Button Examples"),
71+
ui.input_radio_buttons(
72+
"radio2",
73+
"Standard Radio Group",
74+
["Selection 1", "Selection 2", "Selection 3"],
75+
),
76+
ui.input_radio_buttons(
77+
"radio3",
78+
"Inline Radio Group",
79+
["Option 1", "Option 2", "Option 3"],
80+
inline=True,
81+
),
82+
),
83+
ui.card(
84+
ui.card_header("Checkbox Examples"),
85+
ui.input_checkbox_group(
86+
"check1",
87+
"Standard Checkbox Group",
88+
["Item 1", "Item 2", "Item 3"],
89+
),
90+
ui.input_checkbox_group(
91+
"check2",
92+
"Inline Checkbox Group",
93+
["Choice A", "Choice B", "Choice C"],
94+
inline=True,
95+
),
96+
),
97+
ui.card(
98+
ui.card_header("Select Input Widgets"),
99+
ui.input_selectize(
100+
"select1",
101+
"Selectize Input",
102+
["Selection A", "Selection B", "Selection C"],
103+
),
104+
ui.input_select(
105+
"select2",
106+
"Multiple Select Input",
107+
["Item X", "Item Y", "Item Z"],
108+
multiple=True,
109+
),
110+
),
111+
ui.card(
112+
ui.card_header("Text Input Widgets"),
113+
ui.input_text("text1", "Text Input"),
114+
ui.input_text_area(
115+
"textarea1",
116+
"Text Area Input",
117+
"Default text content for the text area widget",
118+
),
119+
ui.input_password("password1", "Password Input"),
120+
),
121+
width=400,
122+
heights_equal=False,
123+
),
124+
),
125+
ui.nav_panel(
126+
"Documentation",
127+
ui.div(
128+
ui.markdown(
129+
"""
130+
_Just in case it isn't obvious, this text (and most of this app) was written by an LLM._
131+
132+
# Component Documentation
133+
134+
The Shiny for Python framework, available at [shiny.posit.co/py](https://shiny.posit.co/py/),
135+
provides a comprehensive set of UI components for building interactive web applications. These
136+
components are designed with **consistency and usability** in mind, making it easier for
137+
developers to create professional-grade applications.
138+
139+
Our framework implements the `ui.page_navbar()` component as the primary navigation structure,
140+
allowing for intuitive organization of content across multiple views. Each view can contain
141+
various input and output elements, managed through the `ui.card()` container system.
142+
143+
## Component Architecture
144+
145+
*The architecture of our application framework* emphasizes modularity and reusability. Key
146+
components like `ui.value_box()` and `ui.layout_column_wrap()` work together to create
147+
structured, responsive layouts that adapt to different screen sizes.
148+
149+
<table class="table table-striped">
150+
<thead>
151+
<tr>
152+
<th>Component</th>
153+
<th>Implementation</th>
154+
<th>Use Case</th>
155+
<th>Status</th>
156+
</tr>
157+
</thead>
158+
<tbody>
159+
<tr>
160+
<td>Value Box</td>
161+
<td><code>ui.value_box()</code></td>
162+
<td>Metric Display</td>
163+
<td>Production Ready</td>
164+
</tr>
165+
<tr>
166+
<td>Card</td>
167+
<td><code>ui.card()</code></td>
168+
<td>Content Container</td>
169+
<td>Production Ready</td>
170+
</tr>
171+
<tr>
172+
<td>Layout</td>
173+
<td><code>ui.layout_column_wrap()</code></td>
174+
<td>Component Organization</td>
175+
<td>Production Ready</td>
176+
</tr>
177+
<tr>
178+
<td>Navigation</td>
179+
<td><code>ui.page_navbar()</code></td>
180+
<td>Page Structure</td>
181+
<td>Production Ready</td>
182+
</tr>
183+
</tbody>
184+
</table>
185+
186+
## Implementation Best Practices
187+
188+
When implementing components, maintain consistent patterns in your code. Use the
189+
`@render` decorators for output functions and follow the reactive programming model
190+
with `@reactive.effect` for side effects.
191+
192+
Error handling should be implemented at both the UI and server levels. For input
193+
validation, use the `req()` function to ensure all required values are present
194+
before processing.
195+
196+
## Corporate Brand Guidelines
197+
198+
Effective corporate brand guidelines should accomplish several key objectives:
199+
200+
1. **Visual Consistency**: Establish a clear color palette using our theming system.
201+
Primary colors should be defined using `class_="btn-primary"` and similar Bootstrap
202+
classes.
203+
204+
2. *Typography Standards*: Maintain consistent font usage across all text elements.
205+
Headers should use the built-in styling provided by the `ui.card_header()` component.
206+
207+
3. `Component Styling`: Apply consistent styling to UI elements such as buttons,
208+
cards, and value boxes. Use the theme parameter in components like
209+
`ui.value_box(theme="primary")`.
210+
211+
4. **Layout Principles**: Follow a grid-based layout system using
212+
`ui.layout_column_wrap()` with appropriate width parameters to ensure consistent
213+
spacing and alignment.
214+
215+
5. *Responsive Design*: Implement layouts that adapt gracefully to different screen
216+
sizes using the `fillable` parameter in page components.
217+
218+
Remember that brand guidelines should serve as a framework for consistency while
219+
remaining flexible enough to accommodate future updates and modifications to the
220+
application interface.
221+
"""
222+
),
223+
class_="container-sm",
224+
),
225+
),
226+
ui.nav_spacer(),
227+
ui.nav_control(ui.input_dark_mode(id="color_mode")),
228+
title="brand.yml Demo",
229+
fillable=["Input Output Demo", "Widget Gallery"],
230+
theme=theme,
231+
)
232+
233+
234+
def server(input, output, session):
235+
@render.plot
236+
def plot1():
237+
colors = {
238+
"foreground": "#000000",
239+
"background": "#FFFFFF",
240+
"primary": "#4463ff",
241+
}
242+
243+
if theme.brand.color:
244+
colors.update(theme.brand.color.to_dict("theme"))
245+
246+
if input.color_mode() == "dark":
247+
bg = colors["foreground"]
248+
fg = colors["background"]
249+
colors.update({"foreground": fg, "background": bg})
250+
251+
x = np.linspace(0, input.numeric1(), 100)
252+
y = np.sin(x) * input.slider1()
253+
fig, ax = plt.subplots(facecolor=colors["background"])
254+
ax.plot(x, y, color=colors["primary"])
255+
ax.set_title("Sine Wave Output", color=colors["foreground"])
256+
ax.set_facecolor(colors["background"])
257+
ax.tick_params(colors=colors["foreground"])
258+
for spine in ax.spines.values():
259+
spine.set_edgecolor(colors["foreground"])
260+
spine.set_alpha(0.25)
261+
return fig
262+
263+
@render.text
264+
def out_text1():
265+
return "\n".join(
266+
["def example_function():", ' return "Function output text"']
267+
)
268+
269+
270+
app = App(app_ui, server)

0 commit comments

Comments
 (0)