diff --git a/client/App.jsx b/client/App.jsx
index f211598..c11643c 100644
--- a/client/App.jsx
+++ b/client/App.jsx
@@ -11,6 +11,7 @@ import InputAddon from './layouts/input-add-on/Details';
import MediaObject from './layouts/media-object/Details';
import Menu from './layouts/menu/Details';
import PreviousNextButtons from './layouts/previous-next-buttons/Details';
+import ProgressBar from './layouts/progress-bar/Details';
import SameHeightColumns from './layouts/same-height-columns/Details';
import Sidebar from './layouts/sidebar/Details';
import SplitScreen from './layouts/split-screen/Details';
@@ -32,6 +33,7 @@ const App = () => {
+
diff --git a/client/Home.jsx b/client/Home.jsx
index 5b33cef..07132c3 100644
--- a/client/Home.jsx
+++ b/client/Home.jsx
@@ -10,6 +10,7 @@ import InputAddonCover from './layouts/input-add-on/Cover';
import MediaObjectCover from './layouts/media-object/Cover';
import MenuCover from './layouts/menu/Cover';
import PreviousNextButtonCover from './layouts/previous-next-buttons/Cover';
+import ProgressBarCover from './layouts/progress-bar/Cover';
import SameHeightColumnsCover from './layouts/same-height-columns/Cover';
import SidebarCover from './layouts/sidebar/Cover';
import SplitScreenCover from './layouts/split-screen/Cover';
@@ -17,7 +18,7 @@ import StepperInputCover from './layouts/stepper-input/Cover';
import StickyFooterCover from './layouts/sticky-footer/Cover';
import StickyHeaderCover from './layouts/sticky-header/Cover';
import Layout from './Layout';
-import useDocumentTitle from './useDocumentTitle';
+import useDocumentTitle from './hooks/useDocumentTitle';
const Home = () => {
useDocumentTitle('CSS Layout');
@@ -125,6 +126,12 @@ const Home = () => {
diff --git a/client/useDocumentTitle.js b/client/hooks/useDocumentTitle.js
similarity index 100%
rename from client/useDocumentTitle.js
rename to client/hooks/useDocumentTitle.js
diff --git a/client/hooks/useInterval.js b/client/hooks/useInterval.js
new file mode 100644
index 0000000..cb441ad
--- /dev/null
+++ b/client/hooks/useInterval.js
@@ -0,0 +1,16 @@
+import { useEffect } from 'react';
+
+const useInterval = (callback, delay) => {
+ useEffect(
+ () => {
+ const handler = () => callback();
+ if (delay !== null) {
+ const id = setInterval(handler, delay);
+ return () => clearInterval(id);
+ }
+ },
+ [delay]
+ );
+};
+
+export default useInterval;
diff --git a/client/layouts/badge/Details.jsx b/client/layouts/badge/Details.jsx
index b4ac309..5a5895e 100644
--- a/client/layouts/badge/Details.jsx
+++ b/client/layouts/badge/Details.jsx
@@ -3,7 +3,7 @@ import React from 'react';
import DetailsLayout from '../../DetailsLayout';
import BrowserFrame from '../../placeholders/BrowserFrame';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Badge');
diff --git a/client/layouts/button-with-icon/Details.jsx b/client/layouts/button-with-icon/Details.jsx
index 56951f0..142831f 100644
--- a/client/layouts/button-with-icon/Details.jsx
+++ b/client/layouts/button-with-icon/Details.jsx
@@ -5,7 +5,7 @@ import BrowserFrame from '../../placeholders/BrowserFrame';
import Circle from '../../placeholders/Circle';
import Rectangle from '../../placeholders/Rectangle';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Button with icon');
diff --git a/client/layouts/centering/Details.jsx b/client/layouts/centering/Details.jsx
index 05e55d5..64faacd 100644
--- a/client/layouts/centering/Details.jsx
+++ b/client/layouts/centering/Details.jsx
@@ -5,7 +5,7 @@ import BrowserFrame from '../../placeholders/BrowserFrame';
import Circle from '../../placeholders/Circle';
import Rectangle from '../../placeholders/Rectangle';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Centering');
diff --git a/client/layouts/fixed-at-corner/Details.jsx b/client/layouts/fixed-at-corner/Details.jsx
index 062bcc8..8a37913 100644
--- a/client/layouts/fixed-at-corner/Details.jsx
+++ b/client/layouts/fixed-at-corner/Details.jsx
@@ -4,7 +4,7 @@ import DetailsLayout from '../../DetailsLayout';
import BrowserFrame from '../../placeholders/BrowserFrame';
import Triangle from '../../placeholders/Triangle';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Fixed at corner');
diff --git a/client/layouts/holy-grail/Details.jsx b/client/layouts/holy-grail/Details.jsx
index 1c467ac..dba00b9 100644
--- a/client/layouts/holy-grail/Details.jsx
+++ b/client/layouts/holy-grail/Details.jsx
@@ -5,7 +5,7 @@ import Block from '../../placeholders/Block';
import BrowserFrame from '../../placeholders/BrowserFrame';
import Rectangle from '../../placeholders/Rectangle';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Holy grail');
diff --git a/client/layouts/input-add-on/Details.jsx b/client/layouts/input-add-on/Details.jsx
index 414dd12..55b4a4a 100644
--- a/client/layouts/input-add-on/Details.jsx
+++ b/client/layouts/input-add-on/Details.jsx
@@ -4,7 +4,7 @@ import DetailsLayout from '../../DetailsLayout';
import BrowserFrame from '../../placeholders/BrowserFrame';
import Rectangle from '../../placeholders/Rectangle';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Input add-on');
diff --git a/client/layouts/media-object/Details.jsx b/client/layouts/media-object/Details.jsx
index 7e8cf92..b7a4bbe 100644
--- a/client/layouts/media-object/Details.jsx
+++ b/client/layouts/media-object/Details.jsx
@@ -6,7 +6,7 @@ import BrowserFrame from '../../placeholders/BrowserFrame';
import Rectangle from '../../placeholders/Rectangle';
import Square from '../../placeholders/Square';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Media object');
diff --git a/client/layouts/menu/Details.jsx b/client/layouts/menu/Details.jsx
index e8875bd..fe48eb9 100644
--- a/client/layouts/menu/Details.jsx
+++ b/client/layouts/menu/Details.jsx
@@ -5,7 +5,7 @@ import BrowserFrame from '../../placeholders/BrowserFrame';
import Circle from '../../placeholders/Circle';
import Rectangle from '../../placeholders/Rectangle';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Menu');
diff --git a/client/layouts/previous-next-buttons/Details.jsx b/client/layouts/previous-next-buttons/Details.jsx
index 841974b..2d75a97 100644
--- a/client/layouts/previous-next-buttons/Details.jsx
+++ b/client/layouts/previous-next-buttons/Details.jsx
@@ -4,7 +4,7 @@ import DetailsLayout from '../../DetailsLayout';
import BrowserFrame from '../../placeholders/BrowserFrame';
import Rectangle from '../../placeholders/Rectangle';
import SampleCode from '../../SampleCode';
-import useDocumentTitle from '../../useDocumentTitle';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
const Details = () => {
useDocumentTitle('CSS Layout ∙ Previous and next buttons');
diff --git a/client/layouts/progress-bar/Cover.jsx b/client/layouts/progress-bar/Cover.jsx
new file mode 100644
index 0000000..78bc62e
--- /dev/null
+++ b/client/layouts/progress-bar/Cover.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+
+import Frame from '../../placeholders/Frame';
+
+const Cover = () => {
+ return (
+
+
+
+ );
+};
+
+export default Cover;
diff --git a/client/layouts/progress-bar/Details.jsx b/client/layouts/progress-bar/Details.jsx
new file mode 100644
index 0000000..97ea89b
--- /dev/null
+++ b/client/layouts/progress-bar/Details.jsx
@@ -0,0 +1,68 @@
+import React, { useState } from 'react';
+
+import DetailsLayout from '../../DetailsLayout';
+import BrowserFrame from '../../placeholders/BrowserFrame';
+import SampleCode from '../../SampleCode';
+import useDocumentTitle from '../../hooks/useDocumentTitle';
+import useInterval from '../../hooks/useInterval';
+
+const Details = () => {
+ useDocumentTitle('CSS Layout ∙ Progress bar');
+ const [progress, setProgress] = useState(0);
+ useInterval(() => {
+ setProgress(v => v === 100 ? 0 : v + 1);
+ }, 1 * 100);
+
+ return (
+
+ Progress bar
+
+
+
+ }
+ source={
+