The Skeleton Loading kit can be used an intermediate loading state to give users a visual indication that content is loading.
Please Note: this kit is not meant to be integrated interally within other Playbook kits as a loading prop; rather, it can be used to create a composite of the section/kit/page with loading intermediataries, as demonstrated in the the "example component" doc examples.
The SkeletonLoading component has a default and a white color variant.
Use the stack and gap props in conjunction to layer multiple Skeleton loading bars on top of each other.
stack accepts a number that correlates to the number of rows (1 is default), and gap accepts a portion of our spacing props (xxs as default, xs, sm, md, lg, xl, xxl) to set the pixel distance between each row. gap will not do anything if there is no corresponding stack prop set.
The border_radius prop accepts all of our BorderRadius tokens, with sm as default.
<%= pb_rails("flex", props: { justify: "evenly" }) do %> <%= pb_rails("skeleton_loading", props: { border_radius: "rounded", height: "50px", width: "100px"}) %> <%= pb_rails("skeleton_loading", props: { border_radius: "xl", height: "50px", width: "100px"}) %> <%= pb_rails("skeleton_loading", props: { border_radius: "lg", height: "50px", width: "100px"}) %> <%= pb_rails("skeleton_loading", props: { border_radius: "md", height: "50px", width: "100px"}) %> <%= pb_rails("skeleton_loading", props: { height: "50px", width: "100px"}) %> <%= pb_rails("skeleton_loading", props: { border_radius: "xs", height: "50px", width: "100px"}) %> <%= pb_rails("skeleton_loading", props: { border_radius: "none", height: "50px", width: "100px"}) %> <% end %>
The height and width props accept pixel and percentage values. If using a percentage for height, the parent container must have a set height.
Set the height and width props to the same value to make a square. A rounded border_radius will make a square a circle. If using percentages to make a square, your parent container must also be a square.
<%= pb_rails("skeleton_loading", props: { height: "100px", width: "50%" }) %> <%= pb_rails("skeleton_loading", props: { gap: "md", height: "20px", margin_y: "md", stack: 3, width: "50px" }) %> <%= pb_rails("card", props: { height: "200px", margin_bottom: "md", padding: "none", width: "100%" }) do %> <%= pb_rails("skeleton_loading", props: { border_radius: "md", gap: "xl", height: "50%", width: "300px" }) %> <% end %> <%= pb_rails("card", props: { height: "200px", margin_bottom: "md", padding: "none", width: "100%" }) do %> <%= pb_rails("skeleton_loading", props: { border_radius: "md", gap: "xl", height: "30%", stack: 2, width: "70%" }) %> <% end %> <%= pb_rails("skeleton_loading", props: { height: "150px", margin_y: "md", width: "150px" }) %> <%= pb_rails("skeleton_loading", props: { border_radius: "rounded", height: "150px", width: "150px" }) %>
<%= pb_rails("button", props: { id: "toggle-user-button", margin_bottom: "md", text: "Show User", variant: "secondary" }) %> <div id="skeleton-loading-user-content"> <%= pb_rails("flex", props: { align_items: "center" }) do %> <%= pb_rails("skeleton_loading", props: { border_radius: "rounded", height: "38px", padding_right: "sm", width: "38px"}) %> <%= pb_rails("skeleton_loading", props: { gap: "xxs", height: "18px", stack: 2, width: "161px"}) %> <% end %> <%= pb_rails("flex", props: { align_items: "start", padding_top: "md" }) do %> <%= pb_rails("flex", props: { align_items: "center", flex_direction: "column" }) do %> <%= pb_rails("skeleton_loading", props: { border_radius: "rounded", height: "100px", padding_bottom: "xs", width: "100px"}) %> <%= pb_rails("skeleton_loading", props: { height: "32px", padding_bottom: "xxs", width: "144px"}) %> <%= pb_rails("skeleton_loading", props: { height: "21px", width: "164px"}) %> <% end %> <% end %> </div> <div id="user-content" style="display: none;"> <div> <%= pb_rails("user", props: { name: "Anna Black", title: "Remodeling Consultant", orientation: "horizontal", align: "left", avatar_url: "https://randomuser.me/api/portraits/women/44.jpg" }) %> </div> <div> <%= pb_rails("flex", props: { align_items: "start", padding_top: "md" }) do %> <%= pb_rails("user", props: { name: "Anna Black", title: "Remodeling Consultant", orientation: "vertical", align: "center", size: "lg", avatar_url: "https://randomuser.me/api/portraits/women/44.jpg" }) %> <% end %> </div> </div> <script> document.addEventListener("DOMContentLoaded", function() { const toggleButton = document.getElementById('toggle-user-button') const skeletonUserContentDiv = document.getElementById('skeleton-loading-user-content') const userContentDiv = document.getElementById('user-content') let isLoading = true toggleButton.addEventListener('click', function() { isLoading = !isLoading if (isLoading) { skeletonUserContentDiv.style.display = 'block' userContentDiv.style.display = 'none' toggleButton.textContent = 'Show User' } else { skeletonUserContentDiv.style.display = 'none' userContentDiv.style.display = 'block' toggleButton.textContent = 'Show Skeleton Loading' } }) }) </script>
<%= pb_rails("button", props: { id: "toggle-filter-button", margin_bottom: "md", text: "Show Filter", variant: "secondary" }) %> <div id="skeleton-loading-filter-content"> <%= pb_rails("card", props: { margin_bottom: "lg" }) do %> <%= pb_rails("flex", props: { align_items: "center", justify: "between", orientation: "row" }) do %> <%= pb_rails("flex", props: { align_items: "center", justify: "start", orientation: "row" }) do %> <%= pb_rails("skeleton_loading", props: { border_radius: "rounded", height: "40px", margin_right: "sm", width: "40px" }) %> <%= pb_rails("skeleton_loading", props: { height: "16px", margin_right: "md", width: "80px" }) %> <% end %> <%= pb_rails("flex", props: { align_items: "center", justify: "end", orientation: "row" }) do %> <%= pb_rails("skeleton_loading", props: { height: "18px", width: "120px" }) %> <% end %> <% end %> <% end %> <%= pb_rails("skeleton_loading", props: { height: "127px", width: "100%" }) %> </div> <div id="filter-content" style="display: none;"> <%= pb_rails("filter", props: { margin_bottom: "xl", min_width: "360px", id: "2", filters: [ { name: "name", value: "John Wick" } ], sort_menu: [ { item: "Popularity", link: "?q[sorts]=managers_popularity+asc", active: true, direction: "desc" }, { item: "Mananger's Title", link: "?q[sorts]=managers_title+asc", active: false }, { item: "Manager's Name", link: "?q[sorts]=managers_name+asc", active: false }, ], results: 546, template: "single" }) do %> <% example_collection = [ OpenStruct.new(name: "USA", value: 1), OpenStruct.new(name: "Canada", value: 2), OpenStruct.new(name: "Brazil", value: 3), OpenStruct.new(name: "Philippines", value: 4), OpenStruct.new(name: "A galaxy far far away...", value: 5) ] %> <%= pb_rails("form", props: { form_system_options: { scope: :example, method: :get } }) do |form| %> <%= form.text_field :example_text_field, props: { label: true } %> <%= form.collection_select :example_collection_select, example_collection, :value, :name, props: { label: true } %> <%= form.actions do |action| %> <%= action.submit props: { text: "Apply", data: { disable_with: "pb_rails('icon', props: { icon: 'spinner', spin: true, fixed_width: true })Searching...".html_safe },}%> <%= action.button props: { type: "reset", text: "Clear", variant: "secondary" } %> <% end %> <% end %> <% end %> <%= pb_rails("filter", props: { min_width: "360px", id: "1", filters: [ { name: "name", value: "John Wick" }, { name: "city", value: "San Francisco"} ], sort_menu: [ { item: "Popularity", link: "?q[sorts]=managers_popularity+asc", active: true, direction: "desc" }, { item: "Mananger's Title", link: "?q[sorts]=managers_title+asc", active: false }, { item: "Manager's Name", link: "?q[sorts]=managers_name+asc", active: false }, ], template: "default", results: 1, }) do %> <% example_collection = [ OpenStruct.new(name: "USA", value: 1), OpenStruct.new(name: "Canada", value: 2), OpenStruct.new(name: "Brazil", value: 3), OpenStruct.new(name: "Philippines", value: 4), OpenStruct.new(name: "A galaxy far far away...", value: 5) ] %> <%= pb_rails("form", props: { form_system_options: { scope: :example, method: :get } }) do |form| %> <%= form.text_field :example_text_field, props: { label: true } %> <%= form.collection_select :example_collection_select, example_collection, :value, :name, props: { label: true } %> <%= form.actions do |action| %> <%= action.submit props: { text: "Apply", data: { disable_with: "pb_rails('icon', props: { icon: 'spinner', spin: true, fixed_width: true })Searching...".html_safe },}%> <%= action.button props: { type: "reset", text: "Clear", variant: "secondary" } %> <% end %> <% end %> <% end %> </div> <script> document.addEventListener("DOMContentLoaded", function() { const toggleButton = document.getElementById('toggle-filter-button') const skeletonFilterContentDiv = document.getElementById('skeleton-loading-filter-content') const filterContentDiv = document.getElementById('filter-content') const secondFilterContentDiv = document.getElementById('second-filter-content') let isLoading = true toggleButton.addEventListener('click', function() { isLoading = !isLoading if (isLoading) { skeletonFilterContentDiv.style.display = 'block' filterContentDiv.style.display = 'none' toggleButton.textContent = 'Show Filter' } else { skeletonFilterContentDiv.style.display = 'none' filterContentDiv.style.display = 'block' toggleButton.textContent = 'Show Skeleton Loading' } }) }) </script>
| Props | Type | Values |
|---|---|---|
align_content |
enum | responsive
|
start
end
center
spaceBetween
spaceAround
spaceEvenly
|
align_items |
enum | responsive
|
start
end
center
|
border_radius |
enum
|
none
xs
sm
md
lg
xl
rounded
|
cursor |
enum
|
auto
default
none
contextMenu
help
pointer
progress
wait
cell
|
dark |
boolean
|
true
false
|
flex |
enum | responsive
|
auto
initial
0
1
2
3
4
5
6
7
8
9
10
11
12
none
|
flex_direction |
enum | responsive
|
row
column
rowReverse
columnReverse
|
flex_wrap |
enum | responsive
|
wrap
nowrap
wrapReverse
|
justify_content |
enum | responsive
|
start
end
center
spaceBetween
spaceAround
spaceEvenly
|
line_height |
enum
|
loosest
looser
loose
normal
tight
tighter
tightest
|
margin_right |
array
|
none
xxs
xs
sm
md
lg
xl
|
margin_left |
array
|
none
xxs
xs
sm
md
lg
xl
|
margin_top |
array
|
none
xxs
xs
sm
md
lg
xl
|
margin_bottom |
array
|
none
xxs
xs
sm
md
lg
xl
|
margin_x |
array
|
none
xxs
xs
sm
md
lg
xl
|
margin_y |
array
|
none
xxs
xs
sm
md
lg
xl
|
margin |
array
|
none
xxs
xs
sm
md
lg
xl
|
width |
string
|
|
min_width |
string
|
|
max_width |
string
|
|
gap |
string | responsive
|
|
column_gap |
string | responsive
|
|
row_gap |
string | responsive
|
|
number_spacing |
enum
|
tabular
|
order |
enum | responsive
|
none
first
1
2
3
4
5
6
7
8
9
10
11
12
|
overflow_x |
enum
|
scroll
visible
hidden
auto
|
overflow_y |
enum
|
scroll
visible
hidden
auto
|
overflow |
enum
|
scroll
visible
hidden
auto
|
padding_right |
array
|
none
xxs
xs
sm
md
lg
xl
|
padding_left |
array
|
none
xxs
xs
sm
md
lg
xl
|
padding_top |
array
|
none
xxs
xs
sm
md
lg
xl
|
padding_bottom |
array
|
none
xxs
xs
sm
md
lg
xl
|
padding_x |
array
|
none
xxs
xs
sm
md
lg
xl
|
padding_y |
array
|
none
xxs
xs
sm
md
lg
xl
|
padding |
array
|
none
xxs
xs
sm
md
lg
xl
|
position |
enum
|
relative
absolute
fixed
sticky
static
|
shadow |
enum
|
none
deep
deeper
deepest
|
text_align |
enum | responsive
|
start
end
left
right
center
justify
justifyAll
matchParent
|
truncate |
enum
|
none
1
2
3
4
5
|
vertical_align |
enum | responsive
|
baseline
super
top
middle
bottom
sub
text-top
text-bottom
|
z_index |
enum | responsive
|
1
2
3
4
5
6
7
8
9
10
max
|
top |
enum | object
|
xxs
xs
sm
md
lg
xl
xxl
|
inset |
boolean
|
true
false
|
right |
enum | object
|
xxs
xs
sm
md
lg
xl
xxl
|
bottom |
enum | object
|
xxs
xs
sm
md
lg
xl
xxl
|
left |
enum | object
|
xxs
xs
sm
md
lg
xl
xxl
|
height |
string
|
|
max_height |
string
|
|
min_height |
string
|
|
hover |
object
|
|
group_hover |
boolean
|
true
false
|
| Props | Type | Values | Default |
|---|---|---|---|
stack |
number
|
1
|
|
color |
enum
|
default
white
|
default
|