diff --git a/assets/__demo_variables.css b/assets/__demo_variables.css index 49258ce..6edb3e4 100644 --- a/assets/__demo_variables.css +++ b/assets/__demo_variables.css @@ -24,21 +24,25 @@ Dash reads all css files contained in `/assets/` so no imports are necessary. --blue-bright: #03B8FF; --blue-light: #2A7DE1; --blue-dark: #074C91; - --red-light: #C70039; - --red-dark: #900C3F; + --blue-darker: #2d4376;; + --blue-darkest: #202239; + --red-light: #f57677; + --red-dark: #aa3a3c; --orange: #FF7006; - --teal-light: #06ECDC; + --teal-light: #06ecdc; + --teal: #17bebb; --teal-dark: #17BEBB; --teal-darker: #008C82; - --grey-lighter: #EEEEEE; - --grey-light: #DDDDDD; + --grey-lighter: #f5f7fb; + --grey-light: #DBDBDB; --grey: #AAAAAA; --grey-medium: #737373; --grey-dark: #222222; --box-shadow: 0 0 1rem rgba(66, 82, 121, 0.2); --font: "proxima-nova", "Helvetica Neue", sans-serif; - --banner-height: 3.5rem; + --banner-height: 6.5rem; --left-col-width: 26.25rem; --problem-details-height: 11.25rem; + --title-section-height: 13.5rem; /*** Add new variables here ***/ } diff --git a/assets/__style_guide.css b/assets/__style_guide.css index 56f5525..603ddaa 100644 --- a/assets/__style_guide.css +++ b/assets/__style_guide.css @@ -21,8 +21,7 @@ limitations under the License. } body { - background-color: #f9f9f9; - color: var(--grey-dark); + color: var(--grey-darkest); font-size: 1.125rem; line-height: 1.5rem; margin: 0; @@ -31,17 +30,19 @@ body { h1, h2, h3, h4, h5, h6, td, th, span, a, p, label, button, input { font-family: var(--font); + color: var(--blue-darkest); } h1, h2, h3, h4, h5, h6 { - color: var(--blue-dark); - font-weight: 400; + font-weight: 600; + margin-top: 0; } h1 { font-size: 2.5rem; line-height: 3rem; margin-bottom: 1rem; + font-weight: 400; } h2 { @@ -62,6 +63,8 @@ h5 { font-size: 1.125rem; margin: 1rem 0; font-weight: 600; + margin-top: 1rem; + margin-bottom: 1rem; } label { @@ -79,19 +82,31 @@ hr { table { margin-bottom: 1.25rem; font-size: 1rem; - border: 1px solid var(--grey-light); border-collapse: collapse; + border: 1px solid var(--grey-light); +} + +thead { + border-bottom: 1px solid var(--grey-light); + font-weight: 600; + background-color: var(--blue-darker); +} + +tfoot { + border-top: 1px solid var(--grey-light); + font-weight: 600; } th { font-weight: 600; + color: white; + padding: 0.5rem; + text-align: left; } -th, td { - color: var(--grey-dark); +td { padding: 0.25rem 0.5rem; - border-right: 1px solid var(--grey-light); - border-bottom: 1px solid var(--grey-light); + text-align: left; } th:first-child, td:first-child { @@ -104,8 +119,8 @@ th:last-child, td:last-child { input[type="checkbox"], input[type="radio"] { - accent-color: var(--blue-dark); - margin: 0 0.6rem 0 0; + accent-color: var(--blue-darker); + margin: 0 0.5rem 0 0; } .display-none { @@ -120,9 +135,9 @@ input[type="radio"] { .banner { height: var(--banner-height); - box-sizing: border-box; - background-color: var(--blue-dark); - padding: 1rem 2rem; + background-color: #12131f; + padding: 0 2rem; + border-bottom: 2px solid var(--teal); display: flex; align-items: center; @@ -130,7 +145,7 @@ input[type="radio"] { } .banner img { - height: 100%; + height: 1rem; } .columns-main { @@ -140,25 +155,51 @@ input[type="radio"] { .left-column { display: flex; - height: calc(100vh - var(--banner-height)); + height: 100%; + background-color: var(--grey-lighter); + box-shadow: var(--box-shadow); } .right-column { - background-color: var(--grey-lighter); - padding: 1.25rem 0 0; width: 100%; min-height: 40rem; } +.title-section { + padding: 2rem 1rem 1.5rem; + background-color: var(--blue-darkest); + background-image: url("background.svg"); + background-position: 30% 0; + height: var(--title-section-height); +} + +.title-section h1 { + color: white; +} + +.title-section p { + margin-bottom: 0; + color: white; +} + .settings { - margin: 1rem 0 1.5rem; + margin: 1rem 0 2rem; +} + +.form-section { + display: flex; + flex: 1; +} + +.settings-and-buttons-wrapper { + padding: 0 1rem 2rem; + flex: 1; } .radio label, .checklist label { margin-top: 0.125rem; font-weight: 400; - font-size: 0.875rem; } .radio--inline label:first-child, @@ -172,7 +213,7 @@ input[type="radio"] { } .is-focused:not(.is-open) > .Select-control { - border-color: var(--orange); + border-color: var(--teal); box-shadow: none; } @@ -196,7 +237,7 @@ div.dash-sk-circle { /* Make the skip link visible when it receives keyboard focus */ .skip-link:focus { - color: var(--orange); + color: var(--teal); position: static; width: auto; height: auto; diff --git a/assets/_buttons.css b/assets/_buttons.css index e4d2a54..93354d2 100644 --- a/assets/_buttons.css +++ b/assets/_buttons.css @@ -17,24 +17,34 @@ limitations under the License. /* Style rules for buttons */ button { - font-size: 1rem; - line-height: 1rem; - padding: 1rem 2rem; - height: auto; - color: white; - transition: all 0.2s ease-in-out; border: none; - background-color: var(--blue-dark); + font-size: 0.875rem; font-weight: 600; - text-transform: uppercase; - border-radius: 0.25rem; + line-height: 1.75; + border-radius: 4px; + padding: 0.75rem 1.5rem; cursor: pointer; - letter-spacing: .1rem; + transition: all 0.2s ease-in-out; } -button:hover { - filter: brightness(80%); +.button { + font-size: 0.875rem; + font-weight: 600; + line-height: 1.75; + border-radius: 4px; + padding: 0.75rem 1.5rem; color: white; + text-transform: uppercase; + background: radial-gradient(61.22% 95.86% at 26.96% 100%, #4c71c6 0%, var(--blue-darker) 100%); + transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1), + border-color 250ms cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: rgba(29, 30, 36, 0.19) 0px 5px 14px 0px; + border: 1px solid rgba(76, 113, 198, 0.4); +} + +.button:hover { + background: radial-gradient(61.22% 95.86% at 26.96% 100%, var(--blue-darker) 0%, var(--blue-darker) 100%); } #run-button, @@ -43,11 +53,12 @@ button:hover { } #cancel-button { - background-color: var(--red-light); + background: radial-gradient(61.22% 95.86% at 26.96% 100%, var(--red-light) 0%, var(--red-dark) 100%); + border: 1px solid rgb(245, 118, 119, 0.4); } #cancel-button:hover { - background-color: var(--red-dark); + background: radial-gradient(61.22% 95.86% at 26.96% 100%, var(--red-dark) 0%, var(--red-dark) 100%); } button:disabled { @@ -57,5 +68,5 @@ button:disabled { } button:focus-visible { - box-shadow: 0 0 0 4px var(--orange); + box-shadow: 0 0 0 4px var(--teal); } diff --git a/assets/_collapse.css b/assets/_collapse.css index 300f1d0..f75b40a 100644 --- a/assets/_collapse.css +++ b/assets/_collapse.css @@ -16,25 +16,63 @@ limitations under the License. /* Style rules for collapsible dropdowns like left-column-collapse and problem-details-collapse */ +.collapse-arrow { + height: 1.5rem; + position: relative; + width: 1.4rem; + transition: border-color 0.25s ease-in-out; +} + +.collapse-arrow:before, +.collapse-arrow:after { + content: ""; + display: block; + position: absolute; + height: 0.25rem; + width: 1.5rem; + background: var(--blue-darker); + border-radius: 0.125rem; +} + +.collapse-arrow:before { + rotate: -70deg; + top: 0; +} + +.collapse-arrow:after { + rotate: 70deg; + bottom: 0; +} + +.settings-and-buttons-wrapper { + height: calc(100vh - var(--title-section-height)); + overflow-y: auto; + direction: rtl; +} + +.settings-and-buttons { + direction: ltr; +} + .left-column .left-column-layer-1 { width: var(--left-col-width); - transition: width 0.6s ease-in-out; - overflow-x: hidden; - overflow-y: auto; + transition: width 0.5s ease-in-out; direction: rtl; } .details-to-collapse { height: var(--problem-details-height); - transition: height 0.6s ease-in-out; + transition: height 0.5s ease-in-out; overflow: hidden; } .left-column .left-column-layer-2 { - padding: 0 1rem 2.5rem; width: var(--left-col-width); box-sizing: border-box; direction: ltr; + display: flex; + flex-direction: column; + height: 100%; } .details-collapse-wrapper { @@ -42,43 +80,37 @@ limitations under the License. overflow: hidden; } -.left-column-collapse, -.left-column-collapse:hover, -.left-column-collapse:focus { +.left-column-collapse { background: white; - border-right: 1px solid var(--grey-lighter); + border: none; height: 100%; border-radius: 0; - padding: 0 0 0 0.25rem; - filter: none; + padding: 0; display: block; } +.left-column-collapse:hover, +.left-column-collapse:focus { + background: var(--grey-lighter); +} + .details-collapse, .details-collapse:hover, .details-collapse:focus { background: none; display: flex; + align-items: center; padding: 0 1.25rem 0 0; text-transform: none; -} - -.collapse-arrow { - border-right: 4px solid var(--grey-medium); - border-bottom: 4px solid var(--grey-medium); - transform: rotate(135deg) skew(165deg, 165deg); - height: 1.25rem; - width: 1.25rem; - margin-right: -3px; - transition: border-color 0.25s ease-in-out; + color: var(--blue-dark); } .details-collapse .collapse-arrow { - transform: rotate(225deg) skew(165deg, 165deg); - margin: 1rem 0 0 1rem; - border-color: var(--blue-dark); - height: 0.75rem; - width: 0.75rem; + transform: rotate(90deg); + width: 3rem; + margin-top: 1.5rem; + margin-left: 0.5rem; + color: var(--blue-darkest); } .left-column-collapse:hover .collapse-arrow { @@ -90,14 +122,24 @@ limitations under the License. } .collapsed .left-column-collapse .collapse-arrow { - margin-left: -3px; - margin-right: 0; - transform: rotate(315deg) skew(165deg, 165deg); + margin-right: -4px; + transform: rotate(180deg); +} + +.title-section { + transition: all 0.5s ease-in-out; +} + +.collapsed .title-section { + height: 0; + padding: 0; + overflow: hidden; } .collapsed .details-collapse .collapse-arrow { - margin-top: 1rem; - transform: rotate(45deg) skew(165deg, 165deg); + margin-bottom: 1.5rem; + margin-top: 0; + transform: rotate(-90deg); } .collapsed .details-to-collapse { @@ -105,14 +147,13 @@ limitations under the License. } .collapsed .left-column-layer-1 { - width: 0; + width: 1.5rem; } @media screen and (max-width: 1000px) { .left-column { height: auto; width: 100%; - flex-direction: column; } .left-column .left-column-layer-1, @@ -122,11 +163,16 @@ limitations under the License. } .left-column-collapse .collapse-arrow { - transform: rotate(45deg); + transform: rotate(90deg); margin: auto; } - .collapsed .left-column-layer-1 { + .collapsed .title-section { + height: 0; + overflow-y: hidden; + } + + .collapsed .settings-and-buttons-wrapper { height: 0; flex: unset; padding: 0; @@ -135,7 +181,6 @@ limitations under the License. .collapsed .left-column-collapse .collapse-arrow { margin-right: auto; - margin-left: auto; - transform: rotate(-135deg); + transform: rotate(-90deg); } } diff --git a/assets/_inputs.css b/assets/_inputs.css index 53f0213..623b929 100644 --- a/assets/_inputs.css +++ b/assets/_inputs.css @@ -65,10 +65,10 @@ limitations under the License. /* Style rules for the dmc.NumberInput element */ .mantine-NumberInput-input:focus-visible { - box-shadow: 0 0 0 2px var(--orange); + box-shadow: 0 0 0 2px var(--teal); } /* Style rules for the dmc.Select element */ .mantine-Select-input:focus-visible { - box-shadow: 0 0 0 2px var(--orange); + box-shadow: 0 0 0 2px var(--teal); } diff --git a/assets/_tabs.css b/assets/_tabs.css index 26ccbb6..db07e2a 100644 --- a/assets/_tabs.css +++ b/assets/_tabs.css @@ -16,48 +16,63 @@ limitations under the License. /* Style rules for tabs like the input and results tabs */ -.tab-container { - border-bottom: 3px solid var(--blue-light); - flex-direction: row; +nav { + height: 100%; +} + +.mantine-Tabs-root { + height: 100%; +} + +.mantine-Tabs-panel { + height: calc(100% - var(--banner-height)); } -.tab-parent .tab { - margin: 0 1.25rem; - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; +.mantine-Tabs-list { + --tabs-list-gap: 1.5rem; + height: 100%; +} + +.mantine-Tabs-list::before { border: none; } -div.tab.tab--selected { - border: 3px solid var(--blue-light) !important; - border-bottom: none !important; - cursor: default; - box-shadow: 0 6px 0 -3px white; +.mantine-Tabs-tab { + font-size: 1.5rem; + font-weight: 400; + border-radius: 0; + border-bottom-width: 0; + border-top-width: 5px; + height: 100%; } -div.tab.tab--disabled { - cursor: not-allowed !important; - color: var(--grey-medium) !important; +button.mantine-Tabs-tab:disabled { + filter: unset; + opacity: 1; + color: #7E7F86; + border: transparent; } -.tab:first-child { - margin-right: 0.625rem; +.mantine-Tabs-tab span { + color: #A0A1A5; + opacity: 1; + transition: color 0.2s ease-in-out; } -.tab:last-child { - margin-left: 0.625rem; +.mantine-Tabs-tab:where([data-active]) span { + opacity: 1; + color: white; } -.tab-parent { - display: flex; - flex-direction: column; +.mantine-Tabs-tab:hover { + background-color: unset; } -.tab-content, -.tab-parent { - height: 100%; +.mantine-Tabs-tab:hover:where(:not([data-active])) { + border-color: unset; } -.tab-content { - background-color: white; +.mantine-Tabs-tab:hover:where(:not(:disabled)) span { + opacity: 1; + color: white; } diff --git a/assets/background.svg b/assets/background.svg new file mode 100644 index 0000000..d8ad4ad --- /dev/null +++ b/assets/background.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demo_callbacks.py b/demo_callbacks.py index d1d3068..ff0fc8d 100644 --- a/demo_callbacks.py +++ b/demo_callbacks.py @@ -69,7 +69,7 @@ def render_initial_state(slider_value: int) -> str: Returns: str: The content of the input tab. """ - return f"Put demo input here. The current slider value is {slider_value}." + return f"Put demo visuals here. The current slider value is {slider_value}." @dash.callback( @@ -89,8 +89,8 @@ def render_initial_state(slider_value: int) -> str: State("radio", "value"), ], running=[ - (Output("cancel-button", "className"), "", "display-none"), # Show/hide cancel button. - (Output("run-button", "className"), "display-none", ""), # Hides run button while running. + (Output("cancel-button", "style"), {}, {"display": "none"}), # Show/hide cancel button. + (Output("run-button", "style"), {"display": "none"}, {}), # Hides run button while running. (Output("results-tab", "disabled"), True, False), # Disables results tab while running. (Output("results-tab", "label"), "Loading...", "Results"), (Output("tabs", "value"), "input-tab", "input-tab"), # Switch to input tab while running. diff --git a/demo_interface.py b/demo_interface.py index 62cfe2d..f3c2c1d 100644 --- a/demo_interface.py +++ b/demo_interface.py @@ -39,7 +39,7 @@ def slider(label: str, id: str, config: dict) -> html.Div: Args: label: The title that goes above the slider. id: A unique selector for this element. - config: A dictionary of slider configerations, see dcc.Slider Dash docs. + config: A dictionary of slider configurations, see dcc.Slider Dash docs. """ return html.Div( className="slider-wrapper", @@ -197,12 +197,12 @@ def generate_run_buttons() -> html.Div: return html.Div( id="button-group", children=[ - html.Button(id="run-button", children="Run Optimization", n_clicks=0, disabled=False), + html.Button("Run Optimization", id="run-button", className="button"), html.Button( + "Cancel Optimization", id="cancel-button", - children="Cancel Optimization", - n_clicks=0, - className="display-none", + className="button", + style={"display": "none"}, ), ], ) @@ -279,11 +279,10 @@ def create_interface(): href="#main-content", id="skip-to-main", className="skip-link", + tabIndex=1, ), # Below are any temporary storage items, e.g., for sharing data between callbacks. dcc.Store(id="run-in-progress", data=False), # Indicates whether run is in progress - # Header brand banner - html.Header(className="banner", children=[html.Img(src=THUMBNAIL, alt="D-Wave logo")]), # Settings and results columns html.Main( className="columns-main", @@ -300,40 +299,82 @@ def create_interface(): html.Div( className="left-column-layer-2", # Padding and content wrapper children=[ - html.H1(MAIN_HEADER), - html.P(DESCRIPTION), - generate_settings_form(), - generate_run_buttons(), + html.Div( + [ + html.H1(MAIN_HEADER), + html.P(DESCRIPTION), + ], + className="title-section", + ), + html.Div( + [ + html.Div( + html.Div( + [ + generate_settings_form(), + generate_run_buttons(), + ], + className="settings-and-buttons", + ), + className="settings-and-buttons-wrapper", + ), + # Left column collapse button + html.Div( + html.Button( + id={ + "type": "collapse-trigger", + "index": 0, + }, + className="left-column-collapse", + title="Collapse sidebar", + children=[ + html.Div(className="collapse-arrow") + ], + **{"aria-expanded": "true"}, + ), + ), + ], + className="form-section", + ), ], ) ], ), - # Left column collapse button - html.Div( - html.Button( - id={"type": "collapse-trigger", "index": 0}, - className="left-column-collapse", - title="Collapse sidebar", - children=[html.Div(className="collapse-arrow")], - **{"aria-expanded": "true"}, - ), - ), ], ), # Right column html.Div( className="right-column", children=[ - dcc.Tabs( + dmc.Tabs( id="tabs", value="input-tab", - mobile_breakpoint=0, + color="white", children=[ - dcc.Tab( - label="Input", - id="input-tab", - value="input-tab", # for switching tabs programatically - className="tab", + html.Header( + className="banner", + children=[ + html.Nav( + [ + dmc.TabsList( + [ + dmc.TabsTab("Input", value="input-tab"), + dmc.TabsTab( + "Results", + value="results-tab", + id="results-tab", + disabled=True, + ), + ] + ), + ] + ), + html.Img(src=THUMBNAIL, alt="D-Wave logo"), + ], + ), + dmc.TabsPanel( + value="input-tab", + tabIndex="12", children=[ html.Div( className="tab-content-wrapper", @@ -349,11 +390,9 @@ def create_interface(): ) ], ), - dcc.Tab( - label="Results", - id="results-tab", - className="tab", - disabled=True, + dmc.TabsPanel( + value="results-tab", + tabIndex="13", children=[ html.Div( className="tab-content-wrapper", diff --git a/src/demo_enums.py b/src/demo_enums.py index 132aa06..a61c963 100644 --- a/src/demo_enums.py +++ b/src/demo_enums.py @@ -31,5 +31,5 @@ def label(self): }[self] -### If any settings or variables are being used repeatedly, thoughout the code, create a new +### If any settings or variables are being used repeatedly, throughout the code, create a new ### Enum for the setting here to avoid string comparisons or other fragile code practices. diff --git a/static/demo.png b/static/demo.png index 4969c82..2d706af 100644 Binary files a/static/demo.png and b/static/demo.png differ