diff --git a/cypress/integration/theme.spec.js b/cypress/integration/theme.spec.js
new file mode 100644
index 000000000..d6223ca55
--- /dev/null
+++ b/cypress/integration/theme.spec.js
@@ -0,0 +1,94 @@
+const A11Y_OPTS = {
+ runOnly: {
+ type: "tag",
+ values: ["section508", "best-practice", "wcag21aa", "wcag2aa"]
+ }
+};
+
+context("Accessibility (a11y)", () => {
+ context("Logged out", () => {
+ it.skip("overview", () => {
+ cy.clearSession();
+ cy.setTheme("theme-light");
+ cy.visit("/account/login");
+ cy.injectAxe();
+ cy.wait(500);
+ cy.checkA11y(A11Y_OPTS);
+ });
+ });
+
+ context("Logged in", () => {
+ beforeEach(() => {
+ cy.clearSession();
+ cy.setTheme("theme-light");
+ cy.server();
+ // overview page
+ cy.route("GET", "*api/v1/repos*", "fixture:overview_page.json");
+ // add repos page
+ cy.route(
+ "GET",
+ "*api/v1/user/source/repos*",
+ "fixture:add_repositories.json"
+ );
+ // settings page
+ cy.route("GET", "*api/v1/repos/*/octocat", "fixture:repository.json");
+ // repo and build page
+ cy.stubBuilds();
+ cy.stubBuild();
+ cy.stubStepsWithLogs();
+ // hooks page
+ cy.route("GET", "*api/v1/hooks/github/octocat*", "fixture:hooks_5.json");
+ cy.route(
+ "GET",
+ "*api/v1/repos/*/octocat/builds/1*",
+ "fixture:build_success.json"
+ );
+ cy.route(
+ "GET",
+ "*api/v1/repos/*/octocat/builds/2*",
+ "fixture:build_failure.json"
+ );
+ cy.route(
+ "GET",
+ "*api/v1/repos/*/octocat/builds/3*",
+ "fixture:build_running.json"
+ );
+ });
+ after(() => {
+ cy.visit("/");
+ cy.server({ enable: false });
+ });
+
+ it.skip("overview", () => {
+ cy.checkA11yForPage("/", A11Y_OPTS);
+ });
+
+ it.skip("add repos", () => {
+ cy.checkA11yForPage("/account/add-repos", A11Y_OPTS);
+ });
+
+ it.skip("settings", () => {
+ cy.checkA11yForPage("/github/octocat/settings", A11Y_OPTS);
+ });
+
+ it.skip("repo page", () => {
+ cy.checkA11yForPage("/someorg/somerepo", A11Y_OPTS);
+ });
+
+ it.skip("hooks page", () => {
+ cy.login("/github/octocat/hooks");
+ cy.injectAxe();
+ cy.wait(500);
+ cy.get("[data-test=hook]").click({ multiple: true });
+ cy.checkA11y(A11Y_OPTS);
+ });
+
+ it.skip("build page", () => {
+ cy.login("/someorg/somerepo/1");
+ cy.injectAxe();
+ cy.wait(500);
+ cy.get("[data-test=step-header]").click({ multiple: true });
+ cy.checkA11y(A11Y_OPTS);
+ });
+ });
+});
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 6f15a8d74..79100f278 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -186,3 +186,9 @@ Cypress.Commands.add("checkA11yForPage", (path = "/", opts = {}) => {
cy.wait(500);
cy.checkA11y(opts);
});
+
+Cypress.Commands.add("setTheme", theme => {
+ cy.window().then(win => {
+ win.localStorage.setItem("vela-theme", theme);
+ });
+});
diff --git a/src/elm/Build.elm b/src/elm/Build.elm
index 7b9bd21c6..702f8df37 100755
--- a/src/elm/Build.elm
+++ b/src/elm/Build.elm
@@ -528,7 +528,7 @@ buildStatusStyles markdown buildStatus buildNumber =
[ div [ class "build-animation", class "-not-running", statusToClass buildStatus ] []
]
in
- List.append animation markdown
+ markdown ++ animation
{-| topParticles : returns an svg frame to parallax scroll on a running build, set to the top of the build
diff --git a/src/elm/Interop.elm b/src/elm/Interop.elm
index a55950e6f..88bbaf286 100644
--- a/src/elm/Interop.elm
+++ b/src/elm/Interop.elm
@@ -4,21 +4,35 @@ Use of this source code is governed by the LICENSE file in this repository.
--}
-port module Interop exposing (onSessionChange, storeSession)
+port module Interop exposing (onSessionChange, onThemeChange, setTheme, storeSession)
import Json.Decode as Decode
import Json.Encode as Encode
--- inbound port
+-- SESSION
+{-| inbound
+-}
port onSessionChange : (Decode.Value -> msg) -> Sub msg
+{-| outbound
+-}
+port storeSession : Encode.Value -> Cmd msg
--- outbound port
-port storeSession : Encode.Value -> Cmd msg
+-- THEME
+
+
+{-| inbound
+-}
+port onThemeChange : (Decode.Value -> msg) -> Sub msg
+
+
+{-| outbound
+-}
+port setTheme : Encode.Value -> Cmd msg
diff --git a/src/elm/Main.elm b/src/elm/Main.elm
index f57fb8107..12eb27e1e 100644
--- a/src/elm/Main.elm
+++ b/src/elm/Main.elm
@@ -108,6 +108,7 @@ import Vela
, Step
, StepNumber
, Steps
+ , Theme(..)
, UpdateRepositoryPayload
, User
, Viewing
@@ -115,6 +116,7 @@ import Vela
, buildUpdateRepoIntPayload
, buildUpdateRepoStringPayload
, decodeSession
+ , decodeTheme
, defaultAddRepositoryPayload
, defaultBuilds
, defaultHooks
@@ -122,7 +124,9 @@ import Vela
, defaultSession
, encodeAddRepository
, encodeSession
+ , encodeTheme
, encodeUpdateRepository
+ , stringToTheme
)
@@ -138,6 +142,7 @@ type alias Flags =
, velaFeedbackURL : String
, velaDocsURL : String
, velaSession : Maybe Session
+ , velaTheme : String
}
@@ -165,6 +170,7 @@ type alias Model =
, inTimeout : Maybe Int
, entryURL : Url
, hookBuilds : HookBuilds
+ , theme : Theme
}
@@ -226,6 +232,7 @@ init flags url navKey =
, inTimeout = Nothing
, entryURL = url
, hookBuilds = Dict.empty
+ , theme = stringToTheme flags.velaTheme
}
( newModel, newPage ) =
@@ -240,6 +247,9 @@ init flags url navKey =
( newModel
, Cmd.batch
[ newPage
+
+ -- for themes, we rely on ports to apply the class on
+ , Interop.setTheme <| encodeTheme model.theme
, setTimeZone
, setTime
]
@@ -259,6 +269,7 @@ type Msg
| ChangeRepoTimeout String
| RefreshSettings Org Repo
| ClickHook Org Repo BuildNumber
+ | SetTheme Theme
| ClickLogLine Org Repo BuildNumber StepNumber Int
| ClickStep Org Repo (Maybe BuildNumber) (Maybe StepNumber)
| GotoPage Pagination.Page
@@ -584,6 +595,13 @@ update msg model =
, action
)
+ SetTheme theme ->
+ if theme == model.theme then
+ ( model, Cmd.none )
+
+ else
+ ( { model | theme = theme }, Interop.setTheme <| encodeTheme theme )
+
ClickLogLine org repo buildNumber stepNumber lineNumber ->
let
( steps, action ) =
@@ -714,6 +732,7 @@ subscriptions : Model -> Sub Msg
subscriptions model =
Sub.batch
[ Interop.onSessionChange decodeOnSessionChange
+ , Interop.onThemeChange decodeOnThemeChange
, every Util.oneSecondMillis <| Tick OneSecond
, every Util.fiveSecondsMillis <| Tick (FiveSecond <| refreshData model)
]
@@ -734,6 +753,16 @@ decodeOnSessionChange sessionJson =
SessionChanged Nothing
+decodeOnThemeChange : Decode.Value -> Msg
+decodeOnThemeChange inTheme =
+ case Decode.decodeValue decodeTheme inTheme of
+ Ok theme ->
+ SetTheme theme
+
+ Err _ ->
+ SetTheme Dark
+
+
{-| refreshPage : refreshes Vela data based on current page and build status
-}
refreshPage : Model -> RefreshData -> Cmd Msg
@@ -917,7 +946,7 @@ view model =
in
{ title = "Vela - " ++ title
, body =
- [ lazy2 viewHeader model.session { feedbackLink = model.velaFeedbackURL, docsLink = model.velaDocsURL }
+ [ lazy2 viewHeader model.session { feedbackLink = model.velaFeedbackURL, docsLink = model.velaDocsURL, theme = model.theme }
, viewNav model
, div [ class "util" ] [ Build.viewBuildHistory model.time model.zone model.page model.builds.org model.builds.repo model.builds.builds ]
, main_ []
@@ -1425,8 +1454,8 @@ navButton model =
text ""
-viewHeader : Maybe Session -> { feedbackLink : String, docsLink : String } -> Html Msg
-viewHeader maybeSession { feedbackLink, docsLink } =
+viewHeader : Maybe Session -> { feedbackLink : String, docsLink : String, theme : Theme } -> Html Msg
+viewHeader maybeSession { feedbackLink, docsLink, theme } =
let
session : Session
session =
@@ -1452,13 +1481,28 @@ viewHeader maybeSession { feedbackLink, docsLink } =
]
]
, div [ class "help-links" ]
- [ a [ href feedbackLink, attribute "aria-label" "go to feedback" ] [ text "feedback" ]
+ [ viewThemeToggle theme
+ , a [ href feedbackLink, attribute "aria-label" "go to feedback" ] [ text "feedback" ]
, a [ href docsLink, attribute "aria-label" "go to docs" ] [ text "docs" ]
, FeatherIcons.terminal |> FeatherIcons.withSize 18 |> FeatherIcons.toHtml []
]
]
+viewThemeToggle : Theme -> Html Msg
+viewThemeToggle theme =
+ let
+ ( newTheme, icon, themeAria ) =
+ case theme of
+ Dark ->
+ ( Light, SvgBuilder.themeLight, "activate light mode" )
+
+ Light ->
+ ( Dark, SvgBuilder.themeDark, "activate dark mode" )
+ in
+ button [ class "theme-toggle", attribute "aria-label" themeAria, onClick (SetTheme newTheme) ] [ icon 24 ]
+
+
-- HELPERS
diff --git a/src/elm/Main/index.d.ts b/src/elm/Main/index.d.ts
index b5a9f9345..9e3203502 100644
--- a/src/elm/Main/index.d.ts
+++ b/src/elm/Main/index.d.ts
@@ -57,6 +57,8 @@ export type Flags = {
readonly velaDocsURL: string;
/** @property velaSession used for passsing in an existing Vela session to Elm */
readonly velaSession: Session | null;
+ /** @property velaTheme: Theme | null */
+ readonly velaTheme: Theme;
};
/**
@@ -66,6 +68,8 @@ export type Flags = {
export type Ports = {
readonly storeSession: ToJS;
readonly onSessionChange: ToElm;
+ readonly setTheme: ToJS;
+ readonly onThemeChange: ToElm;
};
/**
@@ -86,7 +90,7 @@ export type ToElm = {
};
/**
- * The format of the session that we are working with in Vela
+ * The shape of session that we are working with in Vela
*
*/
export type Session = {
@@ -94,3 +98,9 @@ export type Session = {
readonly token: string;
readonly entrypoint: string;
};
+
+/**
+ * Supported themes
+ *
+ */
+export type Theme = "theme-light" | "theme-dark";
diff --git a/src/elm/SvgBuilder.elm b/src/elm/SvgBuilder.elm
index 18c5fd51b..a4d19e99d 100644
--- a/src/elm/SvgBuilder.elm
+++ b/src/elm/SvgBuilder.elm
@@ -24,6 +24,8 @@ module SvgBuilder exposing
, stepRunning
, stepStatusToIcon
, stepSuccess
+ , themeDark
+ , themeLight
, velaLogo
)
@@ -69,22 +71,53 @@ velaLogo size =
]
+themeDark : Int -> Html msg
+themeDark size =
+ svg
+ [ width <| String.fromInt size
+ , height <| String.fromInt size
+ , viewBox "0 0 24 24"
+ , class "theme-dark-icon"
+ ]
+ [ Svg.path [ d "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" ] []
+ ]
+
+
+themeLight : Int -> Html msg
+themeLight size =
+ svg
+ [ width <| String.fromInt size
+ , height <| String.fromInt size
+ , stroke "currentColor"
+ , strokeWidth "2"
+ , viewBox "0 0 24 24"
+ , class "theme-light-icon"
+ ]
+ [ Svg.circle [ cx "12", cy "12", r "5" ] []
+ , Svg.line [ x1 "12", y1 "1", x2 "12", y2 "3" ] []
+ , Svg.line [ x1 "12", y1 "21", x2 "12", y2 "23" ] []
+ , Svg.line [ x1 "4.22", y1 "4.22", x2 "5.64", y2 "5.64" ] []
+ , Svg.line [ x1 "18.36", y1 "18.36", x2 "19.78", y2 "19.78" ] []
+ , Svg.line [ x1 "1", y1 "12", x2 "3", y2 "12" ] []
+ , Svg.line [ x1 "21", y1 "12", x2 "23", y2 "12" ] []
+ , Svg.line [ x1 "4.22", y1 "19.78", x2 "5.64", y2 "18.36" ] []
+ , Svg.line [ x1 "18.36", y1 "5.64", x2 "19.78", y2 "4.22" ] []
+ ]
+
+
{-| buildPending : produces svg icon for build status - pending
-}
buildPending : Html msg
buildPending =
svg
[ class "build-icon -pending"
- , strokeWidth "2"
, viewBox "0 0 408 408"
, width "44"
, height "44"
, ariaHidden
]
- [ Svg.g [ class "status-svg", class "-build-status-icon", class "-pending" ]
- [ Svg.path [ d "M51 153c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51zm306 0c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51zm-153 0c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51z" ]
- []
- ]
+ [ Svg.path [ d "M51 153c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51zm306 0c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51zm-153 0c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51z" ]
+ []
]
@@ -93,17 +126,15 @@ buildPending =
buildRunning : Html msg
buildRunning =
svg
- [ class "build-icon"
+ [ class "build-icon -running"
, strokeWidth "2"
, viewBox "0 0 44 44"
, width "44"
, height "44"
, ariaHidden
]
- [ Svg.g [ class "status-svg" ]
- [ Svg.path [ class "-linecap-round", d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] []
- , Svg.path [ class "-linecap-square", d "M22 10v12.75L30 27" ] []
- ]
+ [ Svg.path [ d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] []
+ , Svg.path [ d "M22 10v12.75L30 27" ] []
]
@@ -112,17 +143,15 @@ buildRunning =
buildSuccess : Html msg
buildSuccess =
svg
- [ class "build-icon"
+ [ class "build-icon -success"
, strokeWidth "2"
, viewBox "0 0 44 44"
, width "44"
, height "44"
, ariaHidden
]
- [ Svg.g [ class "status-svg", class "-linecap-square" ]
- [ Svg.path [ d "M15 20.1l6.923 6.9L42 5" ] []
- , Svg.path [ class "-linecap-round", d "M43 22v16.333A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1h25.666" ] []
- ]
+ [ Svg.path [ d "M15 20.1l6.923 6.9L42 5" ] []
+ , Svg.path [ d "M43 22v16.333A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1h25.666" ] []
]
@@ -131,17 +160,15 @@ buildSuccess =
buildFailure : Html msg
buildFailure =
svg
- [ class "build-icon"
+ [ class "build-icon -failure"
, strokeWidth "2"
, viewBox "0 0 44 44"
, width "44"
, height "44"
, ariaHidden
]
- [ Svg.g [ class "status-svg" ]
- [ Svg.path [ class "-linecap-round", d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] []
- , Svg.path [ class "-linecap-square", d "M15 15l14 14M29 15L15 29" ] []
- ]
+ [ Svg.path [ d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] []
+ , Svg.path [ d "M15 15l14 14M29 15L15 29" ] []
]
@@ -164,8 +191,8 @@ buildStatusAnimation dashes y classNames =
List.append classes
[ class "build-animation"
, strokeWidth "4"
- , width "144"
- , height "144"
+ , width ""
+ , height "4"
, viewBox ""
, ariaHidden
]
@@ -182,16 +209,16 @@ stepPending : Html msg
stepPending =
svg
[ class "-icon -pending"
- , strokeWidth "2"
, viewBox "0 0 408 408"
, width "32"
, height "32"
, ariaHidden
]
- [ Svg.g [ class "status-svg", class "-step-icon", class "-pending" ]
- [ Svg.path [ d "M51 153c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51zm306 0c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51zm-153 0c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51z" ]
- []
+ [ Svg.path
+ [ attribute "vector-effect" "non-scaling-stroke"
+ , d "M51 153c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51zm306 0c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51zm-153 0c-28.05 0-51 22.95-51 51s22.95 51 51 51 51-22.95 51-51-22.95-51-51-51z"
]
+ []
]
@@ -200,17 +227,23 @@ stepPending =
stepRunning : Html msg
stepRunning =
svg
- [ class "-icon"
+ [ class "-icon -running"
, strokeWidth "2"
, viewBox "0 0 44 44"
, width "32"
, height "32"
, ariaHidden
]
- [ Svg.g [ class "status-svg", class "-step-icon", class "-running" ]
- [ Svg.path [ class "-linecap-round", d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] []
- , Svg.path [ class "-linecap-square", d "M22 10v12.75L30 27" ] []
+ [ Svg.path
+ [ attribute "vector-effect" "non-scaling-stroke"
+ , d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z"
]
+ []
+ , Svg.path
+ [ attribute "vector-effect" "non-scaling-stroke"
+ , d "M22 10v12.75L30 27"
+ ]
+ []
]
@@ -219,17 +252,23 @@ stepRunning =
stepSuccess : Html msg
stepSuccess =
svg
- [ class "-icon"
+ [ class "-icon -success"
, strokeWidth "2"
, viewBox "0 0 44 44"
, width "32"
, height "32"
, ariaHidden
]
- [ Svg.g [ class "status-svg", class "-linecap-square", class "-step-icon", class "-success" ]
- [ Svg.path [ d "M15 20.1l6.923 6.9L42 5" ] []
- , Svg.path [ class "-linecap-round", d "M43 22v16.333A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1h25.666" ] []
+ [ Svg.path
+ [ attribute "vector-effect" "non-scaling-stroke"
+ , d "M15 20.1l6.923 6.9L42 5"
]
+ []
+ , Svg.path
+ [ attribute "vector-effect" "non-scaling-stroke"
+ , d "M43 22v16.333A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1h25.666"
+ ]
+ []
]
@@ -238,17 +277,23 @@ stepSuccess =
stepFailure : Html msg
stepFailure =
svg
- [ class "-icon"
+ [ class "-icon -failure"
, strokeWidth "2"
, viewBox "0 0 44 44"
, width "32"
, height "32"
, ariaHidden
]
- [ Svg.g [ class "status-svg", class "-step-icon", class "-failure" ]
- [ Svg.path [ class "-linecap-round", d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] []
- , Svg.path [ class "-linecap-square", d "M15 15l14 14M29 15L15 29" ] []
+ [ Svg.path
+ [ attribute "vector-effect" "non-scaling-stroke"
+ , d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z"
+ ]
+ []
+ , Svg.path
+ [ attribute "vector-effect" "non-scaling-stroke"
+ , d "M15 15l14 14M29 15L15 29"
]
+ []
]
@@ -259,17 +304,14 @@ hookSuccess =
svg
[ class "hook-status"
, class "-success"
- , class "-no-fill"
, strokeWidth "2"
, viewBox "0 0 44 44"
, width "20"
, height "20"
, ariaHidden
]
- [ Svg.g []
- [ Svg.path [ d "M15 20.1l6.923 6.9L42 5" ] []
- , Svg.path [ d "M43 22v16.333A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1h25.666" ] []
- ]
+ [ Svg.path [ attribute "vector-effect" "non-scaling-stroke", d "M15 20.1l6.923 6.9L42 5" ] []
+ , Svg.path [ attribute "vector-effect" "non-scaling-stroke", d "M43 22v16.333A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1h25.666" ] []
]
@@ -280,16 +322,13 @@ hookFailure =
svg
[ class "hook-status"
, class "-failure"
- , class "-no-fill"
, strokeWidth "2"
, viewBox "0 0 44 44"
, width "20"
, height "20"
]
- [ Svg.g []
- [ Svg.path [ d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] []
- , Svg.path [ d "M15 15l14 14M29 15L15 29" ] []
- ]
+ [ Svg.path [ attribute "vector-effect" "non-scaling-stroke", d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] []
+ , Svg.path [ attribute "vector-effect" "non-scaling-stroke", d "M15 15l14 14M29 15L15 29" ] []
]
@@ -299,19 +338,11 @@ buildHistoryPending : Int -> Html msg
buildHistoryPending _ =
svg
[ class "-icon -pending"
- , strokeWidth "2"
, viewBox "0 0 28 28"
- , width "28"
- , height "28"
- ]
- [ Svg.g [ class "build-history-svg", class "-build-history-icon", class "-pending" ]
- [ Svg.path [ class "-path-1", d "M-281-124h1440V900H-281z" ] []
- , Svg.g []
- [ Svg.path [ class "-path-2", d "M0 0h28v28H0z" ] []
- , Svg.circle [ class "-path-3", cx "14", cy "14", r "2" ] []
- ]
- ]
+ , width "26"
+ , height "26"
]
+ [ Svg.circle [ cx "14", cy "14", r "2" ] [] ]
{-| buildHistoryRunning : produces svg icon for build history status - running
@@ -322,17 +353,10 @@ buildHistoryRunning _ =
[ class "-icon -running"
, strokeWidth "2"
, viewBox "0 0 28 28"
- , width "28"
- , height "28"
- ]
- [ Svg.g [ class "build-history-svg", class "-build-history-icon", class "-running" ]
- [ Svg.path [ class "-path-1", d "M-309-124h1440V900H-309z" ] []
- , Svg.g []
- [ Svg.path [ class "-path-2", d "M0 0h28v28H0z" ] []
- , Svg.path [ class "-path-3", d "M14 7v7.5l5 2.5" ] []
- ]
- ]
+ , width "26"
+ , height "26"
]
+ [ Svg.path [ d "M14 7v7.5l5 2.5" ] [] ]
{-| buildHistorySuccess : produces svg icon for build history status - running
@@ -343,17 +367,10 @@ buildHistorySuccess _ =
[ class "-icon -success"
, strokeWidth "2"
, viewBox "0 0 28 28"
- , width "28"
- , height "28"
- ]
- [ Svg.g [ class "build-history-svg", class "-build-history-icon", class "-success" ]
- [ Svg.path [ class "-path-1", d "M-113-124h1440V900H-113z" ] []
- , Svg.g []
- [ Svg.path [ class "-path-2", d "M0 0h28v28H0z" ] []
- , Svg.path [ class "-path-3", d "M6 15.9227L10.1026 20 22 7" ] []
- ]
- ]
+ , width "26"
+ , height "26"
]
+ [ Svg.path [ d "M6 15.9227L10.1026 20 22 7" ] [] ]
{-| buildHistoryFailure : produces svg icon for build history status - failure
@@ -364,17 +381,10 @@ buildHistoryFailure _ =
[ class "-icon -failure"
, strokeWidth "2"
, viewBox "0 0 28 28"
- , width "28"
- , height "28"
- ]
- [ Svg.g [ class "build-history-svg", class "-build-history-icon" ]
- [ Svg.path [ class "-path-1", d "M-253-124h1440V900H-253z" ] []
- , Svg.g []
- [ Svg.path [ class "-path-2", d "M0 0h28v28H0z" ] []
- , Svg.path [ class "-path-3", d "M8 8l12 12M20 8L8 20" ] []
- ]
- ]
+ , width "26"
+ , height "26"
]
+ [ Svg.path [ d "M8 8l12 12M20 8L8 20" ] [] ]
{-| radio : produces svg icon for input radio select
@@ -382,29 +392,21 @@ buildHistoryFailure _ =
radio : Bool -> Html msg
radio checked =
svg
- [ class "-icon -radio"
- , strokeWidth "1"
- , viewBox "0 0 28 28"
+ [ class "-icon"
+ , class "-radio"
+ , strokeWidth "2"
+ , viewBox "0 0 30 30"
, width "22"
, height "22"
]
<|
if checked then
- [ Svg.g [ fill "none", Svg.Attributes.fillRule "evenodd" ]
- [ Svg.path [ fill "#282828", d "M-414-909h1440V115H-414z" ] []
- , Svg.g []
- [ Svg.circle [ stroke "#0CF", cx "14", cy "14", r "13.5" ] []
- , Svg.circle [ fill "#0CF", stroke "#0CF", cx "14", cy "14", r "7" ] []
- ]
- ]
+ [ Svg.circle [ cx "15", cy "15", r "13" ] []
+ , Svg.circle [ class "-inner", cx "15", cy "15", r "6" ] []
]
else
- [ Svg.g [ fill "none", Svg.Attributes.fillRule "evenodd" ]
- [ Svg.circle [ stroke "#0CF", cx "14", cy "14", r "13.5" ] []
- , Svg.circle [ stroke "#0CF", cx "14", cy "14", r "13.5" ] []
- , Svg.path [ stroke "none", d "M.5.5h27v27H.5z" ] []
- ]
+ [ Svg.circle [ cx "15", cy "15", r "13" ] []
]
@@ -413,25 +415,18 @@ radio checked =
checkbox : Bool -> Html msg
checkbox checked =
svg
- [ class "-icon -radio"
- , strokeWidth "1"
+ [ class "-icon -check"
+ , strokeWidth "2"
, viewBox "0 0 28 28"
, width "22"
, height "22"
]
<|
if checked then
- [ Svg.g [ fill "none", Svg.Attributes.fillRule "evenodd" ]
- [ Svg.path [ stroke "#0CF", fill "#0CF", d "M.5.5h27v27H.5z" ] []
- , Svg.path [ stroke "#282828", Svg.Attributes.fillRule "2", Svg.Attributes.strokeLinecap "square", d "M6 15.9227L10.1026 20 22 7" ] []
- ]
- ]
+ [ Svg.path [ class "-checked", strokeLinecap "square", d "M6 15.9227L10.1026 20 22 7" ] [] ]
else
- [ Svg.g [ fill "none", Svg.Attributes.fillRule "evenodd" ]
- [ Svg.path [ stroke "#0CF", fill "none", d "M.5.5h27v27H.5z" ] []
- ]
- ]
+ []
{-| statusToIcon : takes build status string and returns Icon from SvgBuilder
diff --git a/src/elm/Vela.elm b/src/elm/Vela.elm
index 946c6b560..b123414ce 100644
--- a/src/elm/Vela.elm
+++ b/src/elm/Vela.elm
@@ -31,6 +31,7 @@ module Vela exposing
, Step
, StepNumber
, Steps
+ , Theme(..)
, UpdateRepositoryPayload
, User
, Viewing
@@ -48,6 +49,7 @@ module Vela exposing
, decodeSourceRepositories
, decodeStep
, decodeSteps
+ , decodeTheme
, decodeUser
, defaultAddRepositoryPayload
, defaultBuilds
@@ -58,7 +60,9 @@ module Vela exposing
, defaultUser
, encodeAddRepository
, encodeSession
+ , encodeTheme
, encodeUpdateRepository
+ , stringToTheme
)
import Dict exposing (Dict)
@@ -73,6 +77,11 @@ import RemoteData exposing (RemoteData(..), WebData)
-- COMMON
+type Theme
+ = Light
+ | Dark
+
+
type alias Org =
String
@@ -90,6 +99,39 @@ type alias StepNumber =
+-- THEME
+
+
+stringToTheme : String -> Theme
+stringToTheme theme =
+ case theme of
+ "theme-light" ->
+ Light
+
+ _ ->
+ Dark
+
+
+decodeTheme : Decoder Theme
+decodeTheme =
+ Decode.string
+ |> Decode.andThen
+ (\str ->
+ Decode.succeed <| stringToTheme str
+ )
+
+
+encodeTheme : Theme -> Encode.Value
+encodeTheme theme =
+ case theme of
+ Light ->
+ Encode.string "theme-light"
+
+ _ ->
+ Encode.string "theme-dark"
+
+
+
-- SESSION
diff --git a/src/scss/_main.scss b/src/scss/_main.scss
new file mode 100644
index 000000000..8e3c6e7e4
--- /dev/null
+++ b/src/scss/_main.scss
@@ -0,0 +1,1731 @@
+// Copyright (c) 2019 Target Brands, Inc. All rights reserved.
+//
+// Use of this source code is governed by the LICENSE file in this repository.
+
+html {
+ font-family: var(--font-family);
+}
+
+body {
+ color: var(--color-text);
+ background-color: var(--color-bg);
+
+ transition-property: background-color, color;
+ transition-duration: 0.2s;
+
+ font-size: 18px;
+}
+
+a {
+ color: var(--color-primary);
+ &:hover {
+ text-decoration: none;
+ }
+}
+
+header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ padding: 1em var(--horizontal-pad);
+
+ border-top-width: 0;
+ border-bottom: var(--line-width) solid;
+ border-left-width: 0;
+ border-image-source: linear-gradient(
+ to right,
+ var(--color-primary) 55%,
+ transparent 55%,
+ transparent 58%,
+ var(--color-primary) 58%,
+ var(--color-primary) 69%,
+ transparent 69%,
+ transparent 75%,
+ var(--color-secondary) 75%,
+ var(--color-secondary) 76%,
+ transparent 76%
+ );
+ border-image-slice: 1;
+ background: radial-gradient(
+ circle at 50% -200%,
+ var(--color-bg-light) -7%,
+ transparent 50%
+ );
+}
+
+.details {
+ > .summary {
+ position: relative;
+
+ overflow: hidden;
+
+ padding-right: 1.5em;
+ padding-bottom: 0.3em;
+
+ cursor: pointer;
+
+ // hide details marker on Chrome
+ &::-webkit-details-marker {
+ display: none !important;
+ }
+
+ //hide details marker on Firefox
+ &:first-of-type {
+ list-style-type: none !important;
+ }
+
+ // super hacky way to remove
+ // marker on details element
+ //
+ // text-indent: -1em;
+ // Firefox - hide marker
+ // &::-webkit-details-marker,
+ // &::marker {
+ // display: none;
+ // color: var(--color-coal);
+ // }
+ // also could work with:
+ // display: block;
+ // nothing seems to work on Chrome
+ }
+ .details-icon-expand {
+ position: absolute;
+ top: 0.2em;
+ right: 0;
+
+ transition: all 0.2s;
+ transform-origin: 50% 49%;
+
+ color: var(--color-primary);
+ }
+ &[open] .details-icon-expand {
+ transform: rotate(0.5turn);
+ }
+}
+
+.identity {
+ display: flex;
+ align-items: center;
+}
+
+.identity-logo-link {
+ margin-right: 1em;
+
+ text-decoration: none;
+ // override global `vertical-align: middle` for SVGs
+ svg {
+ vertical-align: initial;
+ }
+}
+
+// Vela logo
+.vela-logo {
+ &-star {
+ fill: var(--color-lavender);
+ }
+ &-outer {
+ fill: var(--color-cyan);
+ }
+ &-inner {
+ fill: var(--color-text);
+ }
+}
+
+.help-links {
+ display: flex;
+ align-items: center;
+
+ font-size: 80%;
+ > a {
+ position: relative;
+
+ display: inline-block;
+
+ margin-right: 1em;
+ &:not(:last-child)::after {
+ @include slashes;
+ }
+ &:last-child {
+ text-decoration: none;
+
+ font-weight: bold;
+ }
+ }
+}
+
+.cli-help {
+ vertical-align: top;
+}
+
+.identity-name {
+ position: relative;
+ z-index: 9999;
+ ul {
+ position: absolute;
+ top: 1rem;
+ left: 0;
+
+ width: max-content;
+ min-width: 100%;
+ padding: 0;
+
+ list-style: none;
+
+ border: 1px solid var(--color-bg-light);
+ background-color: var(--color-bg-dark);
+ box-shadow: 0 0 2px black;
+
+ font-size: 80%;
+ > li {
+ padding: 0.5em 1em;
+ }
+ @supports (clip-path: inset(50%)) {
+ &::after {
+ position: absolute;
+ top: -5px;
+ left: calc(50% - 6px);
+
+ display: block;
+
+ width: 10px;
+ height: 10px;
+
+ content: "";
+ transform: rotate(135deg);
+
+ border: inherit;
+ border-radius: 0 0 0 0.25em;
+ background-color: inherit;
+
+ clip-path: polygon(0% 0%, 100% 100%, 0% 100%);
+ }
+ }
+ }
+}
+
+.theme-light .identity-name ul {
+ background: var(--color-white);
+}
+
+.content-wrap {
+ margin: 0 var(--horizontal-pad) var(--horizontal-pad);
+}
+
+button,
+.button {
+ margin: 0.5em 0;
+ padding: 0.5em 2em;
+
+ cursor: pointer;
+ text-decoration: none;
+
+ color: var(--color-bg);
+ border: none;
+ background-image: linear-gradient(
+ to right,
+ var(--color-cyan) 0%,
+ var(--color-lavender) 60%,
+ var(--color-cyan) 100%
+ );
+ background-size: 200% auto;
+
+ font-size: 16px;
+ font-weight: bold;
+
+ & + button,
+ + .button {
+ margin-left: 0.5em;
+ }
+ &:hover {
+ background-position: right center;
+ }
+ &:disabled {
+ color: var(--color-bg-dark);
+ border-color: var(--color-bg-light);
+ background-color: var(--color-bg-light);
+ background-image: none;
+ }
+ &.inverted {
+ color: var(--color-primary);
+ border-width: var(--line-width);
+ border-style: solid;
+ border-color: var(--color-primary);
+ background-color: var(--color-bg);
+ background-image: none;
+ &:not([disabled]):hover {
+ color: var(--color-text);
+ border-color: var(--color-text);
+ }
+ &:disabled {
+ pointer-events: none;
+ color: var(--color-bg-light);
+ border-color: var(--color-bg-light);
+ }
+ }
+}
+
+.navigation {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ padding: 0 3em;
+
+ border-top-width: 0;
+ border-bottom: var(--line-width) solid;
+ border-left-width: 0;
+ border-image-source: linear-gradient(
+ to right,
+ var(--color-bg-light) 15%,
+ transparent 15%,
+ transparent 18%,
+ var(--color-bg-light) 18%,
+ var(--color-bg-light) 18.8%,
+ transparent 18.8%
+ );
+ border-image-slice: 1;
+ li {
+ position: relative;
+
+ display: inline-block;
+
+ margin-right: 1em;
+ &:not(:last-child)::after {
+ @include slashes;
+ }
+ &:last-child {
+ text-decoration: none;
+
+ font-weight: bold;
+ }
+ }
+}
+
+.login-source-icon {
+ display: inline-block;
+
+ margin-right: 0.6em;
+}
+
+.overview {
+ line-height: 2em;
+}
+
+.repo-item {
+ position: relative;
+
+ display: flex;
+ justify-content: space-between;
+
+ margin: 2em 0;
+ padding: 0.5em;
+
+ border-top: var(--line-width) solid;
+ border-left: var(--line-width) solid;
+ border-image-source: linear-gradient(
+ to right,
+ var(--color-bg-light) 75%,
+ transparent 75%,
+ transparent 77%,
+ var(--color-bg-light) 77%,
+ var(--color-bg-light) 84%,
+ transparent 84%
+ );
+ border-image-slice: 1;
+ > summary {
+ margin: 0.5em 0 0 1em;
+
+ text-indent: 0;
+ }
+}
+
+.-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ width: 100%;
+ margin: 0.5em 0 0 1em;
+ padding: 0.5em 1em;
+
+ background-color: var(--color-bg-dark);
+}
+
+.theme-light .-item {
+ background-color: var(--color-white);
+ border: 1px solid var(--color-bg-dark);
+}
+
+.-item .-actions {
+ display: flex;
+ align-items: center;
+}
+
+.-actions .-view {
+ padding: 0.33em 2em;
+
+ @extend .button;
+}
+
+.-actions .-view > a {
+ text-decoration: none;
+
+ color: var(--color-bg);
+}
+
+// loading ellipsis
+.loading-ellipsis::after {
+ display: inline-block;
+ overflow: hidden;
+
+ width: 0;
+
+ content: "\2026";
+ /* ellipsis character */
+ animation: ellipsis steps(4, end) 900ms infinite;
+ vertical-align: bottom;
+}
+
+.util {
+ display: flex;
+
+ height: 3em;
+}
+
+.source-actions {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.source-actions .-filter,
+.source-repos .-filter {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ flex-direction: row;
+
+ margin: 0 1em;
+
+ border-bottom: 2px solid var(--color-primary);
+}
+
+.source-actions .-filter input,
+.source-repos .-filter input {
+ width: 100%;
+ padding: 0.5em 0.8em;
+
+ color: var(--color-text);
+ border: none;
+ background: var(--color-bg);
+
+ font-size: 18px;
+ &::placeholder {
+ color: var(--color-bg-light);
+ }
+}
+
+.org .summary {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+ &::-webkit-details-marker {
+ display: flex;
+
+ margin-right: 16px;
+
+ vertical-align: middle;
+ }
+}
+
+.filtered-repos {
+ margin-top: 2em;
+}
+
+.-no-repos {
+ width: 100%;
+ margin: 0.5em 0 0 1em;
+ padding: 1.2em 1em;
+
+ background-color: var(--color-bg-dark);
+}
+
+.org-header {
+ display: flex;
+ align-items: center;
+ flex: 1;
+}
+
+.repo-count {
+ margin-left: 12px;
+ &::before {
+ margin-right: 0.3em;
+
+ content: "[";
+ }
+ &::after {
+ margin-left: 0.3em;
+
+ content: "]";
+ }
+}
+
+.repo-add {
+ display: flex;
+ align-items: center;
+
+ min-width: 150px;
+ margin: 0.425em 0;
+ padding: 0.2em 1em;
+
+ vertical-align: top;
+ > svg {
+ margin-right: 1em;
+ }
+}
+
+.-added-container {
+ display: flex;
+ flex-direction: row;
+}
+
+.repo-add--added {
+ color: var(--color-green);
+ border: var(--line-width) solid var(--color-green);
+
+ @extend .repo-add;
+}
+
+.repo-add--adding {
+ color: var(--color-yellow);
+ border: var(--line-width) solid var(--color-yellow);
+
+ @extend .repo-add;
+}
+
+.repo-add--adding-text {
+ margin-left: 12px;
+}
+
+.repo-add--failed {
+ cursor: pointer;
+
+ color: var(--color-red);
+ border: var(--line-width) solid var(--color-red);
+ background: none;
+
+ @extend .repo-add;
+
+ &:hover svg {
+ transition: transform 0.2s ease-in-out;
+ transform: rotate(70deg);
+ }
+
+ > svg {
+ margin-right: 1em;
+
+ transition: transform 0.2s ease-in-out;
+ }
+}
+
+a.-btn {
+ margin: 0.5em 0;
+ padding: 0.3em 2em;
+
+ text-decoration: none;
+
+ font-size: 16px;
+ font-weight: bold;
+}
+
+.-btn.-inverted,
+button.-inverted {
+ color: var(--color-primary);
+ border-width: var(--line-width);
+ border-style: solid;
+ border-color: var(--color-primary);
+ background-color: var(--color-bg);
+ background-image: none;
+ &:not([disabled]):hover {
+ color: var(--color-text);
+ border-color: var(--color-text);
+ }
+ &:disabled {
+ color: var(--color-bg-light);
+ border-color: var(--color-bg-light);
+ }
+}
+
+.-btn.-repo-timeout {
+ margin: 0;
+ margin-left: 1em;
+ padding: 4px 12px;
+
+ font-size: 12px;
+ font-weight: 300;
+ &:disabled {
+ color: var(--color-bg);
+ border: var(--line-width) solid var(--color-bg-light);
+ background-color: var(--color-bg);
+ background-image: none;
+ &:hover {
+ color: inherit;
+ border-color: inherit;
+ }
+ }
+}
+.nav-buttons {
+ display: flex;
+}
+.-btn.-hooks {
+ margin-right: 0.4em;
+}
+
+.-btn.-overview {
+ margin: 1em 1em 1em 0;
+}
+
+.-btn.-solid,
+button.-solid {
+ color: var(--color-bg);
+ border-width: var(--line-width);
+ border-style: solid;
+ border-color: var(--color-primary);
+ background-color: var(--color-primary);
+ background-image: none;
+ &:hover {
+ color: var(--color-primary);
+ border-color: var(--color-primary);
+ background-color: var(--color-bg);
+ }
+
+ &.loading {
+ color: var(--color-bg-light);
+ border: var(--line-width) dashed var(--color-bg-light);
+ &:hover {
+ color: inherit;
+ border-color: inherit;
+ }
+ }
+}
+
+.-btn.-view {
+ margin-left: 0.4em;
+}
+
+.btn-refresh {
+ &.loading {
+ color: var(--color-bg-light);
+ border: var(--line-width) dashed var(--color-bg-light);
+ &:not([disabled]):hover {
+ color: inherit;
+ border-color: inherit;
+ }
+ }
+}
+
+.btn-login {
+ display: inline-flex;
+ align-items: center;
+}
+
+// breadcrumb styles
+.crumb {
+ font-weight: 300;
+}
+
+.crumb--current {
+ font-weight: bold;
+}
+
+// builds styles for /:org/:repo/:build_number
+.builds {
+ display: flex;
+ flex-direction: column;
+}
+
+.large-loader {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+}
+
+.large-loader .-spinner {
+ width: 40px;
+ height: 40px;
+
+ animation: spin 3s linear infinite;
+
+ border: 2px solid var(--color-text);
+ border-top: 2px solid var(--color-bg);
+ border-radius: 50%;
+}
+
+.small-loader {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+}
+
+.small-loader .-spinner {
+ width: 20px;
+ height: 20px;
+
+ animation: spin 3s linear infinite;
+
+ border: 2px solid var(--color-offwhite);
+ border-top: 2px solid var(--color-bg);
+ border-radius: 50%;
+}
+
+.small-loader .-label {
+ margin-left: 0.8em;
+
+ font-size: 14px;
+ font-weight: 300;
+}
+
+.build-container {
+ overflow: hidden;
+
+ width: 100%;
+ margin: 12px 0;
+}
+
+.build {
+ position: relative;
+
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+
+ border: 2px solid var(--color-bg-dark);
+ border-right: 0px;
+ border-left: 0px;
+
+ font-family: Helvetica;
+ font-size: 18px;
+ font-weight: 300;
+}
+
+.build .status {
+ position: relative;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+
+ margin-top: -2px;
+ margin-bottom: -2px;
+}
+
+.build-icon {
+ margin: 36px;
+ stroke: var(--color-bg);
+
+ * {
+ fill: none;
+ }
+}
+
+.build-icon.-pending {
+ padding: 8px;
+ fill: var(--color-bg);
+ border: 2px solid var(--color-bg-dark);
+ border-radius: 7px;
+}
+
+.-build-status-icon.-pending {
+ fill: var(--color-bg);
+}
+
+.build .status.-pending {
+ background: var(--color-bg-light);
+}
+
+.build .status.-running {
+ background: var(--color-yellow);
+}
+
+.build .status.-success {
+ background: var(--color-green);
+}
+
+.build .status.-failure,
+.build .status.-error {
+ background: var(--color-red);
+}
+
+.build .info {
+ position: relative;
+
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ justify-content: center;
+
+ padding: 12px 0;
+
+ background: var(--color-bg-dark);
+}
+
+.theme-light .build .info {
+ background: var(--color-white);
+ border: 1px solid var(--color-bg-dark);
+ border-left: none;
+}
+
+.build .row {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+
+ padding: 0 24px;
+}
+
+.build .error {
+ color: var(--color-red-light);
+
+ font-size: 16px;
+}
+
+.theme-light {
+ .build .error {
+ color: var(--color-red);
+ }
+}
+
+.build .error .message {
+ margin-left: 0.2em;
+}
+
+.git-info {
+ display: flex;
+ flex-direction: row;
+}
+
+.git-info .commit {
+ margin: 0 8px 0 0px;
+}
+
+.git-info .branch {
+ margin: 0 8px 0 8px;
+}
+
+.git-info .sender {
+ margin: 0 8px 0 8px;
+}
+
+.time-info {
+ display: flex;
+ flex-direction: row;
+
+ font-weight: 300;
+}
+
+.time-info .age {
+ margin: 0 4px 0 4px;
+}
+
+.time-info .delimiter {
+ margin: 0 8px;
+
+ color: var(--color-secondary);
+}
+
+.time-info .duration {
+ margin: 0 0 0 4px;
+
+ font-family: var(--font-code);
+}
+
+.build-animation {
+ position: absolute;
+
+ width: 100%;
+}
+
+.-running-start {
+ stroke: none;
+}
+
+.-running-particles {
+ stroke: var(--color-yellow);
+}
+
+.build-animation.-bottom {
+ bottom: 0;
+}
+
+.build-animation.-bottom.-running {
+ animation: build-status-parallax-running 26s linear 26s infinite,
+ build-status-parallax-start 26s linear none;
+}
+
+.build-animation.-top.-running {
+ animation: build-status-parallax-running 22s linear 22s infinite,
+ build-status-parallax-start 22s linear none;
+}
+
+.build-animation.-bottom.-start {
+ animation: build-status-parallax-start 26s linear none;
+}
+
+.build-animation.-top.-start {
+ animation: build-status-parallax-start 22s linear none;
+}
+
+.build-animation.-top.-cover {
+ width: 12vw;
+
+ animation: build-particles-source 5s ease-in-out infinite;
+ animation-direction: alternate;
+}
+
+.build-animation.-bottom.-cover {
+ width: 16vw;
+
+ animation: build-particles-source 5s ease-in-out infinite;
+ animation-direction: alternate-reverse;
+}
+
+.build-animation.-running.-frame-0 {
+ left: 0%;
+}
+
+.build-animation.-running.-frame-1 {
+ left: -100%;
+}
+
+.build-animation.-running.-frame-2 {
+ left: -200%;
+}
+
+.build.-success {
+ border-top: 2px solid var(--color-green);
+ border-bottom: 2px solid var(--color-green);
+}
+
+.build.-failure {
+ border-top: 2px solid var(--color-red);
+ border-bottom: 2px solid var(--color-red);
+}
+
+.-animation-dashes-1 {
+ stroke-dasharray: 20 220 5 360;
+}
+
+.-animation-dashes-2 {
+ stroke-dasharray: 70 270 8 300;
+}
+
+.-animation-dashes-3 {
+ stroke-dasharray: 1 240 8 220 12 400 10 180;
+}
+
+.build-history {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+
+ margin-left: 3em;
+}
+
+.build-history .-build {
+ position: relative;
+}
+
+.build-history .-build .-icon {
+ stroke: var(--color-bg);
+ fill: none;
+ &.-running {
+ background-color: var(--color-yellow);
+ }
+
+ &.-failure,
+ &.-error {
+ background-color: var(--color-red);
+ }
+
+ &.-success {
+ background-color: var(--color-green);
+ }
+}
+
+.build-history .-build .-tooltip {
+ position: absolute;
+ // not sure what to here to display the tooltip on top!
+ z-index: 9999;
+ top: 125%;
+
+ display: flex;
+ visibility: hidden;
+ flex-direction: column;
+
+ width: 300px;
+ padding: 0.2em 0;
+
+ text-align: center;
+
+ color: var(--color-text);
+ border: solid 1px var(--color-bg-light);
+ border-radius: 3px;
+ background-color: var(--color-bg-dark);
+
+ font-weight: 300;
+}
+
+.build-history .-build:hover .-tooltip {
+ visibility: visible;
+}
+
+.build-history .-build:hover .-tooltip::after {
+ position: absolute;
+ bottom: 100%; /* At the top of the tooltip */
+
+ margin-left: 0.5em;
+
+ content: " ";
+
+ border-width: 5px;
+ border-style: solid;
+ border-color: transparent transparent var(--color-bg-light) transparent;
+}
+
+.build-history .-build .-info {
+ padding: 0.2em 0.6em;
+
+ font-size: 14px;
+}
+
+.build-history .-build .-line {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.build-history .-build .-line.-header {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.build-history .-build .-line .-label {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+}
+
+.build-history .-build .-number {
+ margin-right: 0.5em;
+ &:before {
+ content: "#";
+ }
+}
+
+.build-history .-build .-event {
+ font-style: italic;
+}
+
+.steps {
+ position: relative;
+}
+
+.step {
+ display: flex;
+}
+
+.step.-line {
+ background: linear-gradient(
+ 90deg,
+ hsla(0, 0, 0, 0) calc(3.25em - 1px),
+ hsla(0, 0%, 75%, 1) calc(3.25em),
+ hsla(0, 0, 0, 0) calc(3.25em + 1px)
+ );
+}
+.step.-line.-last {
+ background-repeat: no-repeat;
+ background-size: 100% 2em;
+}
+.step .-status {
+ margin-top: 0.65em;
+ margin-right: 1em;
+ margin-left: 2.35em;
+}
+
+.step .-icon-container {
+ padding-top: 12px;
+ padding-bottom: 12px;
+
+ background: var(--color-bg);
+}
+
+.step-status-icon {
+ margin-top: 18px;
+ margin-right: 36px;
+ margin-left: 36px;
+}
+
+.step .-icon.-pending {
+ fill: var(--color-bg-light);
+ padding: 8px;
+
+ border: 2px solid var(--color-bg-light);
+ border-radius: 7px;
+}
+
+.-icon {
+ fill: none;
+ &.-success {
+ stroke: var(--color-green);
+ }
+ &.-running {
+ stroke: var(--color-yellow);
+ }
+ &.-failure,
+ &.-error {
+ stroke: var(--color-red);
+ }
+ &.-pending {
+ stroke: var(--color-bg-light);
+ fill: var(--color-bg-light);
+ }
+}
+
+.step .-view {
+ flex: 1;
+
+ margin: 1em 0;
+ padding: 0em;
+
+ border-top: var(--line-width) solid;
+ border-left: var(--line-width) solid;
+ border-image-source: linear-gradient(
+ to right,
+ var(--color-bg-light) 75%,
+ transparent 75%,
+ transparent 77%,
+ var(--color-bg-light) 77%,
+ var(--color-bg-light) 84%,
+ transparent 84%
+ );
+ border-image-slice: 1;
+}
+
+.step .-view.-running {
+ border-color: var(--color-yellow);
+}
+
+.step .summary {
+ padding-top: 0.35em;
+ padding-bottom: 0.35em;
+}
+
+.step .-info {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+
+ margin-left: 1.2em;
+}
+
+.step .-info .-duration {
+ font-family: var(--font-code);
+}
+
+.loading-logs {
+ padding: 0.5em 0;
+ margin-left: 0.3em;
+ margin-top: 0.3em;
+}
+
+.logs-container {
+ background-color: var(--color-bg-dark);
+ padding: 0.5em 0;
+}
+
+.theme-light .logs-container {
+ background-color: var(--color-white);
+ border-top: 1px solid var(--color-bg-dark);
+ border-right: 1px solid var(--color-bg-dark);
+ border-bottom: 1px solid var(--color-bg-dark);
+}
+
+.logs {
+ font-size: 14px;
+ font-weight: 300;
+}
+
+.logs .line {
+ margin: 0 0.5em;
+ display: flex;
+}
+
+.logs .line .wrapper {
+ padding-left: 1em;
+ flex: 1;
+ display: flex;
+ flex-direction: row;
+}
+
+.logs .wrapper.-focus {
+ background: var(--color-focus);
+}
+
+.logs .line .-line-num {
+ margin-right: 1em;
+
+ color: var(--color-offwhite);
+
+ font-family: var(--font-code);
+ > a {
+ text-decoration: none;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+}
+
+.step-error {
+ color: var(--color-red-light);
+ font-size: 14px;
+ margin-left: 1.5em;
+}
+
+.theme-light .step-error {
+ color: var(--color-red);
+}
+
+.step-error .message {
+ margin-left: 0.2em;
+}
+
+.animated {
+ animation-duration: 1s;
+
+ animation-fill-mode: both;
+}
+
+.bounceInRight {
+ animation-name: bounceInRight;
+}
+
+.fadeOutRightBig {
+ animation-name: fadeOutRightBig;
+}
+
+.alerts {
+ ol {
+ display: flex;
+ flex-direction: column-reverse;
+ }
+
+ li {
+ flex: 0 0 auto;
+ }
+}
+
+.alert-container-attributes {
+ position: fixed;
+ right: 0;
+ bottom: 10px;
+
+ width: 100%;
+ max-width: 530px;
+ margin: 0;
+ padding: 0;
+
+ list-style-type: none;
+}
+
+.alert-item-attributes {
+ max-height: 100px;
+ margin: 1em 1em 0 1em;
+
+ transition: max-height 1.2s, margin-top 1.2s;
+}
+
+.alert-container {
+ width: 500px;
+ padding: 1em;
+
+ cursor: pointer;
+
+ color: white;
+ border-radius: 5px;
+ border-radius: 0px;
+ background-color: var(--color-bg-dark);
+ box-shadow: 0 5px 5px -5px hsla(0, 0%, 0%, 0.5);
+
+ font-size: 14px;
+}
+
+.alert-container .-title {
+ margin: 0;
+
+ font-size: 1em;
+}
+
+.alert-container .-message {
+ display: flex;
+ overflow-y: auto;
+ flex-direction: row;
+ justify-content: space-between;
+
+ max-height: 3.25em;
+ margin-top: 0.25em;
+ margin-bottom: 0;
+
+ font-size: 0.9em;
+}
+
+.alert-container.-success {
+ border: 1px solid var(--color-green);
+}
+
+.alert-container.-warning {
+ border: 1px solid var(--color-yellow);
+}
+
+.alert-container.-error {
+ border: 1px solid var(--color-red);
+}
+
+.hooks {
+ width: 100%;
+ padding-bottom: 0.5em;
+
+ background: var(--color-bg-dark);
+}
+
+.theme-light .hooks {
+ background: var(--color-white);
+}
+
+.hooks .loading {
+ display: flex;
+
+ margin-top: 0.5em;
+ margin-left: 0.4em;
+}
+
+.hooks .row {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+
+ margin: 0.3em 0;
+ > .hook-summary {
+ position: relative;
+
+ overflow: hidden;
+
+ padding: 0;
+
+ cursor: pointer;
+
+ // hide details marker on Chrome
+ &::-webkit-details-marker {
+ display: none !important;
+ }
+
+ //hide details marker on Firefox
+ &:first-of-type {
+ list-style-type: none !important;
+ }
+ }
+ .chevron {
+ margin-right: 0.8em;
+ margin-left: 1em;
+
+ transition: all 0.2s;
+ transform-origin: 50% 49%;
+
+ color: var(--color-primary);
+ }
+ &[open] .chevron {
+ transform: rotate(0.5turn);
+ }
+}
+
+.hooks .row.preview {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+}
+
+.hooks .headers {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+
+ margin-top: 0.3em;
+ padding-top: 0.3em;
+ padding-bottom: 0.3em;
+
+ border-bottom: 1px solid var(--color-bg-light);
+}
+
+.hooks .header {
+ flex: 1;
+
+ text-align: center;
+
+ font-size: 18px;
+}
+
+.hooks .row .preview {
+ margin: 0.4em 0;
+
+ font-size: 14px;
+}
+
+.hook-status {
+ fill: none;
+}
+
+.hook-status.-success {
+ stroke: var(--color-green);
+}
+
+.hook-status.-failure {
+ stroke: var(--color-red);
+}
+
+.hooks .row .cell {
+ align-items: center;
+ flex: 1;
+ justify-content: center;
+
+ text-align: center;
+
+ font-weight: 300;
+}
+
+.hooks .source-id {
+ min-width: 330px;
+}
+
+.hooks .first-cell {
+ width: 84px;
+}
+
+.hooks .row .cell.source-id {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.hooks .row .cell.source-id .text {
+ padding: 3px 12px;
+ flex: 1;
+ text-align: center;
+ background: var(--color-bg);
+}
+
+.preview .status.success {
+ color: var(--color-green);
+}
+
+.preview .status.failure {
+ color: var(--color-red-light);
+}
+
+.theme-light {
+ .preview .status.success {
+ color: var(--color-green-dark);
+ }
+
+ .preview .status.failure {
+ color: var(--color-red);
+ }
+}
+
+.hooks .row .info {
+ display: flex;
+ display: inline-block;
+ flex-direction: row;
+
+ width: 100%;
+ padding: 0.4em 1em;
+
+ border-bottom: 1px solid var(--color-bg-light);
+ border-left: 2px solid var(--color-bg-light);
+
+ font-size: 14px;
+}
+
+.hooks .row:last-child .info {
+ border-bottom: none;
+}
+
+.hooks .row .info.-pending {
+ border-left-color: var(--color-bg-light);
+}
+
+.hooks .row .info.-running {
+ border-left-color: var(--color-yellow);
+}
+
+.hooks .row .info.-success {
+ border-left-color: var(--color-green);
+}
+
+.hooks .row .info.-failure,
+.hooks .row .info.-error {
+ border-left-color: var(--color-red);
+}
+
+.hooks .row .info .element span.-m-l,
+.hooks .row .info .element .-m-l {
+ margin-left: 0.4em;
+}
+
+.hooks .row .info .element span.-m-r,
+.hook-build .details .element .-m-r {
+ margin-right: 0.4em;
+}
+
+.hooks .row .info .element .error-label {
+ color: var(--color-red-light);
+}
+
+.theme-light {
+ .hooks .row .info .element .error-label {
+ color: var(--color-red);
+ }
+}
+
+.hooks .row .info .element .hook-build-status.-pending {
+ color: var(--color-offwhite);
+}
+
+.hooks .row .info .element .hook-build-status.-running {
+ color: var(--color-yellow);
+}
+
+.hooks .row .info .element .hook-build-status.-success {
+ color: var(--color-green);
+}
+
+.hooks .row .info .element .hook-build-status.-failure,
+.hooks .row .info .element .hook-build-status.-error {
+ color: var(--color-red-light);
+}
+
+.theme-light {
+ .hooks .row .info .element .hook-build-status.-running {
+ color: var(--color-yellow-dark);
+ }
+
+ .hooks .row .info .element .hook-build-status.-success {
+ color: var(--color-green-dark);
+ }
+
+ .hooks .row .info .element .hook-build-status.-failure,
+ .hooks .row .info .element .hook-build-status.-error {
+ color: var(--color-red);
+ }
+}
+
+.repo-settings {
+ display: flex;
+ flex-direction: column;
+}
+
+.repo-settings .row {
+ display: flex;
+ flex-direction: row;
+}
+
+.repo-settings .category {
+ width: 22em;
+ margin-right: 2em;
+}
+
+.repo-settings .category .header {
+ margin-bottom: 0.5em;
+ padding-bottom: 0.2em;
+
+ border-bottom: 2px solid var(--color-secondary);
+}
+
+.repo-settings .category .header .text {
+ position: relative;
+ top: 0.1em;
+
+ font-size: 22px;
+ font-weight: bold;
+}
+
+.repo-settings .category .description {
+ font-size: 14px;
+ font-weight: 300;
+}
+
+.inputs {
+ margin: 1em;
+}
+
+.checkbox {
+ margin-bottom: 0.5em;
+}
+
+.radio {
+ display: flex;
+ align-items: center;
+}
+
+.-icon {
+ stroke: var(--color-primary);
+
+ &.-check {
+ background-color: var(--color-primary);
+ stroke: var(--color-bg);
+ }
+
+ &.-check,
+ &.-radio {
+ fill: none;
+ }
+
+ .-inner {
+ fill: var(--color-primary);
+ }
+}
+
+.checkbox .label {
+ font-size: 16px;
+ font-weight: bold;
+}
+
+.checkbox .field-info {
+ margin-left: 0.25em;
+
+ font-weight: 300;
+}
+
+.inputs.repo-timeout input {
+ width: 5em;
+ padding-top: 0.5em;
+ padding-right: 0em;
+ padding-bottom: 0.5em;
+ padding-left: 1em;
+
+ text-align: center;
+
+ color: var(--color-text);
+ border: none;
+ border-bottom: 2px solid var(--color-primary);
+ background: none;
+
+ font-size: 14px;
+}
+
+.repo-timeout input:focus {
+ border-color: var(--color-primary);
+}
+
+.repo-timeout {
+ margin-top: 1em;
+ margin-left: 3em;
+}
+
+.repo-timeout .label {
+ margin-left: 0.75em;
+
+ font-size: 16px;
+ font-weight: 300;
+}
+
+.repo-timeout input:invalid {
+ color: var(--color-red-light);
+
+ caret-color: var(--color-text);
+}
+
+.theme-light .repo-timeout input:invalid {
+ color: var(--color-red);
+
+ caret-color: var(--color-text);
+}
+
+.timeout-help {
+ padding: 8px 12px;
+
+ border-radius: 4px;
+ background: var(--color-bg-light);
+
+ font-size: 14px;
+ font-weight: 300;
+}
+
+.checkbox input[type="checkbox"],
+.checkbox input[type="radio"] {
+ position: relative;
+ right: 1.55em;
+ bottom: 0.1em;
+
+ opacity: 0;
+}
+
+.checkbox label {
+ position: relative;
+}
+
+.checkbox label::before,
+.checkbox label::after {
+ position: absolute;
+}
+
+.checkbox label::before {
+ top: calc(50% - 0.45em);
+ left: -1.875em;
+
+ width: 1.15em;
+ height: 1.15em;
+
+ content: "";
+}
+
+.checkbox.radio label::before {
+ top: calc(50% - 0.575em);
+ left: -1.8em;
+
+ width: 1.15em;
+ height: 1.15em;
+
+ content: "";
+}
+
+.checkbox input[type="checkbox"] + label::after,
+.checkbox input[type="radio"] + label::after {
+ content: none;
+}
+
+.checkbox input[type="checkbox"]:focus + label::before,
+.checkbox input[type="radio"]:focus + label::before {
+ outline: var(--color-primary) auto 5px;
+}
+
+.theme-toggle {
+ display: inline-block;
+ position: relative;
+ margin: 0 1em 0 0;
+ padding: 0;
+ font-weight: normal;
+ background: none;
+ color: var(--color-bg-light);
+ fill: var(--color-bg-light);
+}
+
+.theme-light .theme-toggle {
+ color: var(--color-gray-light);
+ fill: var(--color-gray-light);
+}
+
+.pager-actions {
+ display: flex;
+ justify-content: space-between;
+}
diff --git a/src/scss/_mixins.scss b/src/scss/_mixins.scss
index f41d08052..580b2b61d 100644
--- a/src/scss/_mixins.scss
+++ b/src/scss/_mixins.scss
@@ -2,8 +2,6 @@
//
// Use of this source code is governed by the LICENSE file in this repository.
-@import "variables";
-
@mixin slashes {
position: absolute;
right: -0.7rem;
diff --git a/src/scss/_reset.scss b/src/scss/_reset.scss
index 57a2db9d1..ad4927e77 100644
--- a/src/scss/_reset.scss
+++ b/src/scss/_reset.scss
@@ -248,14 +248,6 @@ img {
border-style: none;
}
-/**
- * Change the fill color to match the text color in all browsers (opinionated).
- */
-
-svg:not([fill]) {
- fill: currentColor;
-}
-
/**
* Hide the overflow in IE.
*/
diff --git a/src/scss/_themes.scss b/src/scss/_themes.scss
new file mode 100644
index 000000000..06e386e54
--- /dev/null
+++ b/src/scss/_themes.scss
@@ -0,0 +1,46 @@
+// Copyright (c) 2019 Target Brands, Inc. All rights reserved.
+//
+// Use of this source code is governed by the LICENSE file in this repository.
+
+// Notes;
+// - don't use --color-secondary for text unless you confirmed contrast
+
+// dark theme (default)
+body,
+body.theme-dark {
+ --color-bg-dark: var(--color-coal-dark);
+ --color-bg: var(--color-coal);
+ --color-bg-light: var(--color-coal-light);
+
+ --color-text: var(--color-offwhite);
+
+ --color-primary-dark: var(--color-cyan-dark);
+ --color-primary: var(--color-cyan);
+ --color-primary-light: var(--color-cyan-light);
+
+ --color-secondary-dark: var(--color-lavender-dark);
+ --color-secondary: var(--color-lavender);
+ --color-secondary-light: var(--color-lavender-light);
+
+ --color-focus: var(--color-slate);
+}
+
+// light theme
+body.theme-light {
+ --color-bg-dark: var(--color-gray-light);
+ --color-bg: var(--color-offwhite);
+ // --color-bg-light: var(--color-white);
+ --color-bg-light: var(--color-bg-dark);
+
+ --color-text: var(--color-coal);
+
+ --color-primary-dark: var(--color-lavender-dark);
+ --color-primary: var(--color-lavender); // ok for text
+ --color-primary-light: var(--color-lavender-light);
+
+ --color-secondary-dark: var(--color-cyan-dark);
+ --color-secondary: var(--color-cyan);
+ --color-secondary-light: var(--color-cyan-light);
+
+ --color-focus: var(--color-bg-dark);
+}
diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss
index 6082c0589..6d160c918 100644
--- a/src/scss/_variables.scss
+++ b/src/scss/_variables.scss
@@ -3,29 +3,37 @@
// Use of this source code is governed by the LICENSE file in this repository.
:root {
- // main colors
- --color-offwhite: hsla(0, 0%, 90%, 1);
- --color-gray: hsla(0, 0%, 34%, 1);
- --color-darkcoal: hsla(0, 0%, 12%, 1);
- --color-coal: hsla(0, 0%, 16%, 1);
- --color-cyan: hsla(192, 100%, 50%, 1);
- --color-lightcyan: hsla(192, 100%, 75%, 1);
- --color-lavender: hsla(286, 29%, 55%, 1);
- --color-scarlet: hsla(8, 100%, 50%, 1);
- --color-slate: hsla(211.8,8.8%,37.8%, 0.5);
-
- // for status indications
- --color-red: hsla(353, 69%, 45%, 1);
- --color-green: hsla(89, 63%, 44%, 1);
- --color-yellow: hsla(56, 100%, 50%, 1);
-
- // dark theme (default)
- --color-bg: var(--color-coal);
- --color-bg-darker: var(--color-darkcoal);
- --color-text: var(--color-offwhite);
- --color-primary: var(--color-cyan);
- --color-secondary: var(--color-lavender);
- --color-focus: var(--color-slate);
+ // primary colors
+ --color-cyan-dark: hsl(192, 100%, 30%); // good for text on offwhite
+ --color-cyan: hsl(192, 100%, 50%); // good for text on coal
+ --color-cyan-light: hsl(192, 100%, 65%);
+ --color-lavender-dark: hsl(286, 29%, 40%);
+ --color-lavender: hsl(286, 29%, 51%); // good for text on offwhite
+ --color-lavender-light: hsl(286, 29%, 65%); // good for text on coal
+
+ // grays
+ --color-coal-dark: hsl(0, 0%, 12%);
+ --color-coal: hsl(0, 0%, 16%); // main dark bg
+ --color-coal-light: hsl(0, 0%, 34%);
+
+ --color-white: hsl(0, 0%, 100%);
+ --color-offwhite: hsl(0, 0%, 98%); // main light bg
+ --color-gray-light: hsl(0, 0%, 85%);
+
+ // status indications
+ --color-red-dark: hsl(353, 77%, 40%);
+ --color-red: hsl(353, 77%, 50%); // passes contrast on light bg
+ --color-red-light: hsl(353, 77%, 66%); // passes contrast on coal bg
+
+ --color-green-dark: hsl(89, 71%, 29.6%); // passes contrast on light
+ --color-green: hsl(89, 71%, 48%); // passes contrast on coal
+ --color-green-light: hsl(89, 71%, 60%);
+
+ --color-yellow-dark: hsl(48, 100%, 27%); // passes contrast on light
+ --color-yellow: hsl(48, 100%, 50%); // passes contrast on dark
+ --color-yellow-light: hsl(48, 100%, 75%);
+
+ --color-slate: hsla(211.8, 8.8%, 37.8%, 0.5);
// some common attributes
--horizontal-pad: 3em;
diff --git a/src/scss/style.scss b/src/scss/style.scss
index 909edd4ab..4f832a3f2 100644
--- a/src/scss/style.scss
+++ b/src/scss/style.scss
@@ -2,1700 +2,9 @@
//
// Use of this source code is governed by the LICENSE file in this repository.
-@import "reset";
@import "variables";
-@import "animations";
+@import "reset";
@import "mixins";
-
-html {
- font-family: var(--font-family);
-}
-
-body {
- color: var(--color-text);
- background-color: var(--color-bg);
-
- font-size: 18px;
-}
-
-a {
- color: var(--color-cyan);
- &:hover {
- text-decoration: none;
- }
-}
-
-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- padding: 1em var(--horizontal-pad);
-
- border-top-width: 0;
- border-bottom: var(--line-width) solid;
- border-left-width: 0;
- border-image-source: linear-gradient(
- to right,
- var(--color-primary) 55%,
- transparent 55%,
- transparent 58%,
- var(--color-primary) 58%,
- var(--color-primary) 69%,
- transparent 69%,
- transparent 75%,
- var(--color-secondary) 75%,
- var(--color-secondary) 76%,
- transparent 76%
- );
- border-image-slice: 1;
- background: radial-gradient(
- circle at 50% -200%,
- var(--color-gray) -7%,
- transparent 50%
- );
-}
-
-.details {
- > .summary {
- position: relative;
-
- overflow: hidden;
-
- padding-right: 1.5em;
- padding-bottom: 0.3em;
-
- cursor: pointer;
-
- // hide details marker on Chrome
- &::-webkit-details-marker {
- display: none !important;
- }
-
- //hide details marker on Firefox
- &:first-of-type {
- list-style-type: none !important;
- }
-
- // super hacky way to remove
- // marker on details element
- //
- // text-indent: -1em;
- // Firefox - hide marker
- // &::-webkit-details-marker,
- // &::marker {
- // display: none;
- // color: var(--color-coal);
- // }
- // also could work with:
- // display: block;
- // nothing seems to work on Chrome
- }
- .details-icon-expand {
- position: absolute;
- top: 0.2em;
- right: 0;
-
- transition: all 0.2s;
- transform-origin: 50% 49%;
-
- color: var(--color-primary);
- }
- &[open] .details-icon-expand {
- transform: rotate(0.5turn);
- }
-}
-
-.identity {
- display: flex;
- align-items: center;
-}
-
-.identity-logo-link {
- margin-right: 1em;
-
- text-decoration: none;
- // override global `vertical-align: middle` for SVGs
- svg {
- vertical-align: initial;
- }
-}
-
-// Vela logo
-.vela-logo {
- &-star {
- fill: var(--color-lavender);
- }
- &-outer {
- fill: var(--color-lightcyan);
- }
- &-inner {
- fill: var(--color-text);
- }
-}
-
-.help-links {
- display: flex;
- align-items: center;
-
- font-size: 80%;
- > a {
- position: relative;
-
- display: inline-block;
-
- margin-right: 1em;
- &:not(:last-child)::after {
- @include slashes;
- }
- &:last-child {
- text-decoration: none;
-
- font-weight: bold;
- }
- }
-}
-
-.cli-help {
- vertical-align: top;
-}
-
-.identity-name {
- position: relative;
- z-index: 9999;
- ul {
- position: absolute;
- top: 1rem;
- left: 0;
-
- width: max-content;
- min-width: 100%;
- padding: 0;
-
- list-style: none;
-
- border: 1px solid var(--color-gray);
- background-color: var(--color-bg-darker);
- box-shadow: 0 0 2px black;
-
- font-size: 80%;
- > li {
- padding: 0.5em 1em;
- }
- @supports (clip-path: inset(50%)) {
- &::after {
- position: absolute;
- top: -5px;
- left: calc(50% - 6px);
-
- display: block;
-
- width: 10px;
- height: 10px;
-
- content: "";
- transform: rotate(135deg);
-
- border: inherit;
- border-radius: 0 0 0 0.25em;
- background-color: inherit;
-
- clip-path: polygon(0% 0%, 100% 100%, 0% 100%);
- }
- }
- }
-}
-
-.content-wrap {
- margin: 0 var(--horizontal-pad);
-}
-
-button,
-.button {
- margin: 0.5em 0;
- padding: 0.5em 2em;
-
- cursor: pointer;
- transition: background 0.5s;
- text-decoration: none;
-
- color: var(--color-bg);
- border: none;
- background-image: linear-gradient(
- to right,
- var(--color-cyan) 0%,
- var(--color-lavender) 60%,
- var(--color-cyan) 100%
- );
- background-size: 200% auto;
-
- font-size: 16px;
- font-weight: bold;
-
- & + button,
- + .button {
- margin-left: 0.5em;
- }
- &:hover {
- background-position: right center;
- }
- &:disabled {
- color: var(--color-darkcoal);
- border-color: var(--color-gray);
- background-color: var(--color-gray);
- background-image: none;
- }
- &.inverted {
- color: var(--color-cyan);
- border-width: var(--line-width);
- border-style: solid;
- border-color: var(--color-cyan);
- background-color: var(--color-bg);
- background-image: none;
- &:hover {
- color: var(--color-lightcyan);
- border-color: var(--color-lightcyan);
- }
- &:disabled {
- color: var(--color-gray);
- border-color: var(--color-gray);
- }
- }
-}
-
-.navigation {
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- padding: 0 3em;
-
- border-top-width: 0;
- border-bottom: var(--line-width) solid;
- border-left-width: 0;
- border-image-source: linear-gradient(
- to right,
- var(--color-gray) 15%,
- transparent 15%,
- transparent 18%,
- var(--color-gray) 18%,
- var(--color-gray) 18.8%,
- transparent 18.8%
- );
- border-image-slice: 1;
- li {
- position: relative;
-
- display: inline-block;
-
- margin-right: 1em;
- &:not(:last-child)::after {
- @include slashes;
- }
- &:last-child {
- text-decoration: none;
-
- font-weight: bold;
- }
- }
-}
-
-.login-source-icon {
- display: inline-block;
-
- margin-right: 0.6em;
-}
-
-.overview {
- line-height: 2em;
-}
-
-.repo-item {
- position: relative;
-
- display: flex;
- justify-content: space-between;
-
- margin: 2em 0;
- padding: 0.5em;
-
- border-top: var(--line-width) solid;
- border-left: var(--line-width) solid;
- border-image-source: linear-gradient(
- to right,
- var(--color-gray) 75%,
- transparent 75%,
- transparent 77%,
- var(--color-gray) 77%,
- var(--color-gray) 84%,
- transparent 84%
- );
- border-image-slice: 1;
- > summary {
- margin: 0.5em 0 0 1em;
-
- text-indent: 0;
- }
-}
-
-.-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- width: 100%;
- margin: 0.5em 0 0 1em;
- padding: 0.5em 1em;
-
- background-color: var(--color-darkcoal);
-}
-
-.-item .-actions {
- display: flex;
- align-items: center;
-}
-
-.-actions .-view {
- padding: 0.33em 2em;
-
- @extend .button;
-}
-
-.-actions .-view > a {
- text-decoration: none;
-
- color: var(--color-bg);
-}
-
-// loading ellipsis
-.loading-ellipsis::after {
- display: inline-block;
- overflow: hidden;
-
- width: 0;
-
- content: "\2026";
- /* ellipsis character */
- animation: ellipsis steps(4, end) 900ms infinite;
- vertical-align: bottom;
-}
-
-.util {
- display: flex;
-
- height: 3em;
-}
-
-.source-actions {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.source-actions .-filter,
-.source-repos .-filter {
- display: flex;
- align-items: center;
- flex: 1;
- flex-direction: row;
-
- margin: 0 1em;
-
- border-bottom: 1px solid var(--color-primary);
-}
-
-.source-actions .-filter input,
-.source-repos .-filter input {
- width: 100%;
- padding: 0.5em 0.8em;
-
- color: var(--color-text);
- border: none;
- background: var(--color-bg);
-
- font-size: 18px;
- &::placeholder {
- color: var(--color-gray);
- }
-}
-
-.org .summary {
- display: flex;
- align-items: center;
- flex-direction: row;
- &::-webkit-details-marker {
- display: flex;
-
- margin-right: 16px;
-
- vertical-align: middle;
- }
-}
-
-.filtered-repos {
- margin-top: 2em;
-}
-
-.-no-repos {
- width: 100%;
- margin: 0.5em 0 0 1em;
- padding: 1.2em 1em;
-
- background-color: var(--color-bg-darker);
-}
-
-.org-header {
- display: flex;
- align-items: center;
- flex: 1;
-}
-
-.repo-count {
- margin-left: 12px;
- &::before {
- margin-right: 0.3em;
-
- content: "[";
- }
- &::after {
- margin-left: 0.3em;
-
- content: "]";
- }
-}
-
-.repo-add {
- display: flex;
- align-items: center;
-
- min-width: 150px;
- margin: 0.425em 0;
- padding: 0.2em 1em;
-
- vertical-align: top;
- > svg {
- margin-right: 1em;
- }
-}
-
-.-added-container {
- display: flex;
- flex-direction: row;
-}
-
-.repo-add--added {
- color: var(--color-green);
- border: var(--line-width) solid var(--color-green);
-
- @extend .repo-add;
-}
-
-.repo-add--adding {
- color: var(--color-yellow);
- border: var(--line-width) solid var(--color-yellow);
-
- @extend .repo-add;
-}
-
-.repo-add--adding-text {
- margin-left: 12px;
-}
-
-.repo-add--failed {
- cursor: pointer;
-
- color: var(--color-red);
- border: var(--line-width) solid var(--color-red);
- background: none;
-
- @extend .repo-add;
-
- &:hover svg {
- transition: transform 0.2s ease-in-out;
- transform: rotate(70deg);
- }
-
- > svg {
- margin-right: 1em;
-
- transition: transform 0.2s ease-in-out;
- }
-}
-
-a.-btn {
- margin: 0.5em 0;
- padding: 0.3em 2em;
-
- text-decoration: none;
-
- font-size: 16px;
- font-weight: bold;
-}
-
-.-btn.-inverted,
-button.-inverted {
- color: var(--color-primary);
- border-width: var(--line-width);
- border-style: solid;
- border-color: var(--color-primary);
- background-color: var(--color-bg);
- background-image: none;
- &:hover {
- color: var(--color-lightcyan);
- border-color: var(--color-lightcyan);
- }
-}
-
-.-btn.-repo-timeout {
- margin: 0;
- margin-left: 1em;
- padding: 4px 12px;
-
- font-size: 12px;
- font-weight: 300;
- &:disabled {
- color: var(--color-offwhite);
- border: var(--line-width) solid var(--color-gray);
- background-color: var(--color-coal);
- background-image: none;
- &:hover {
- color: inherit;
- border-color: inherit;
- }
- }
-}
-.nav-buttons {
- display: flex;
-}
-.-btn.-hooks {
- margin-right: 0.4em;
-}
-
-.-btn.-overview {
- margin: 1em 1em 1em 0;
-}
-
-.-btn.-solid,
-button.-solid {
- transition: 0.1s;
-
- color: var(--color-bg-darker);
- border-width: var(--line-width);
- border-style: solid;
- border-color: var(--color-primary);
- background-color: var(--color-primary);
- background-image: none;
- &:hover {
- color: var(--color-lightcyan);
- border-color: var(--color-lightcyan);
- background-color: var(--color-bg-darker);
- }
-
- &.loading {
- color: var(--color--gray);
- border: var(--line-width) dashed var(--color-gray);
- &:hover {
- color: inherit;
- border-color: inherit;
- }
- }
-}
-
-.-btn.-view {
- margin-left: 0.4em;
-}
-
-.btn-refresh {
- &.loading {
- color: var(--color--gray);
- border: var(--line-width) dashed var(--color-gray);
- &:hover {
- color: inherit;
- border-color: inherit;
- }
- }
-}
-
-.btn-login {
- display: inline-flex;
- align-items: center;
-}
-
-// breadcrumb styles
-.crumb {
- font-weight: 300;
-}
-
-.crumb--current {
- font-weight: bold;
-}
-
-// builds styles for /:org/:repo/:build_number
-.builds {
- display: flex;
- flex-direction: column;
-}
-
-.large-loader {
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
-}
-
-.large-loader .-spinner {
- width: 40px;
- height: 40px;
-
- animation: spin 3s linear infinite;
-
- border: 2px solid var(--color-text);
- border-top: 2px solid var(--color-bg);
- border-radius: 50%;
-}
-
-.small-loader {
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
-}
-
-.small-loader .-spinner {
- width: 20px;
- height: 20px;
-
- animation: spin 3s linear infinite;
-
- border: 2px solid var(--color-offwhite);
- border-top: 2px solid var(--color-bg);
- border-radius: 50%;
-}
-
-.small-loader .-label {
- margin-left: 0.8em;
-
- font-size: 14px;
- font-weight: 300;
-}
-
-.build-container {
- overflow: hidden;
-
- width: 100%;
- margin: 12px 0;
-}
-
-.build {
- position: relative;
-
- display: flex;
- flex-direction: row;
- justify-content: space-between;
-
- border: 2px solid var(--color-bg-darker);
- border-right: 0px;
- border-left: 0px;
-
- font-family: Helvetica;
- font-size: 18px;
- font-weight: 300;
-}
-
-.build .status {
- position: relative;
-
- display: flex;
- flex-direction: column;
- justify-content: space-around;
-
- margin-top: -2px;
- margin-bottom: -2px;
-}
-
-.build-icon {
- margin: 36px;
-}
-
-.build-icon.-pending {
- padding: 8px;
-
- border: 2px solid var(--color-bg-darker);
- border-radius: 7px;
-}
-
-.-build-status-icon.-pending {
- fill: var(--color-bg-darker);
-}
-
-.status-svg {
- stroke: var(--color-bg);
- stroke-width: 2;
- fill: none;
- fill-rule: evenodd;
-}
-
-.status-svg.-linecap-square {
- stroke-linecap: square;
-}
-
-.status-svg.-linecap-round {
- stroke-linecap: round;
-}
-
-.build .status.-pending {
- background: var(--color-gray);
-}
-
-.build .status.-running {
- background: var(--color-yellow);
-}
-
-.build .status.-success {
- background: var(--color-green);
-}
-
-.build .status.-failure,
-.build .status.-error {
- background: var(--color-red);
-}
-
-.build .info {
- position: relative;
-
- display: flex;
- flex: 1;
- flex-direction: column;
- justify-content: center;
-
- padding: 12px 0;
-
- background: var(--color-bg-darker);
-}
-
-.build .row {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
-
- padding: 0 24px;
-}
-
-.build .error {
- color: var(--color-scarlet);
-
- font-size: 16px;
-}
-
-.build .error .message {
- margin-left: 0.2em;
-}
-
-.git-info {
- display: flex;
- flex-direction: row;
-}
-
-.git-info .commit {
- margin: 0 8px 0 0px;
-}
-
-.git-info .branch {
- margin: 0 8px 0 8px;
-}
-
-.git-info .sender {
- margin: 0 8px 0 8px;
-}
-
-.time-info {
- display: flex;
- flex-direction: row;
-
- font-weight: 300;
-}
-
-.time-info .age {
- margin: 0 4px 0 4px;
-}
-
-.time-info .delimiter {
- margin: 0 8px;
-
- color: var(--color-secondary);
-}
-
-.time-info .duration {
- margin: 0 0 0 4px;
-
- font-family: var(--font-code);
-}
-
-.build-animation {
- position: absolute;
-
- width: 100%;
-}
-
-.-running-start {
- stroke: none;
-}
-
-.-running-particles {
- stroke: var(--color-yellow);
-}
-
-.build-animation.-top {
- top: -2px;
-}
-
-.build-animation.-bottom {
- bottom: -2px;
-}
-
-.build-animation.-bottom.-running {
- animation: build-status-parallax-running 26s linear 26s infinite,
- build-status-parallax-start 26s linear none;
-}
-
-.build-animation.-top.-running {
- animation: build-status-parallax-running 22s linear 22s infinite,
- build-status-parallax-start 22s linear none;
-}
-
-.build-animation.-bottom.-start {
- animation: build-status-parallax-start 26s linear none;
-}
-
-.build-animation.-top.-start {
- animation: build-status-parallax-start 22s linear none;
-}
-
-.build-animation.-top.-cover {
- width: 12vw;
-
- animation: build-particles-source 5s ease-in-out infinite;
- animation-direction: alternate;
-}
-
-.build-animation.-bottom.-cover {
- width: 16vw;
-
- animation: build-particles-source 5s ease-in-out infinite;
- animation-direction: alternate-reverse;
-}
-
-.build-animation.-running.-frame-0 {
- left: 0%;
-}
-
-.build-animation.-running.-frame-1 {
- left: -100%;
-}
-
-.build-animation.-running.-frame-2 {
- left: -200%;
-}
-
-.build-animation.-not-running {
- position: absolute;
- top: -2px;
- bottom: -2px;
-
- width: 100%;
-}
-
-.build-animation.-not-running.-failure,
-.build-animation.-not-running.-error {
- border-top: 2px solid var(--color-red);
- border-bottom: 2px solid var(--color-red);
-}
-
-.build-animation.-not-running.-success {
- border-top: 2px solid var(--color-green);
- border-bottom: 2px solid var(--color-green);
-}
-
-.-animation-dashes-1 {
- stroke-dasharray: 20 220 5 360;
-}
-
-.-animation-dashes-2 {
- stroke-dasharray: 70 270 8 300;
-}
-
-.-animation-dashes-3 {
- stroke-dasharray: 1 240 8 220 12 400 10 180;
-}
-
-.build-history {
- display: flex;
- align-items: center;
- flex-direction: row;
-
- margin-left: 3em;
-}
-
-.build-history .-build {
- position: relative;
-}
-
-.build-history .-build .-icon.-running {
- fill: var(--color-yellow);
-}
-
-.build-history .-build .-icon.-failure,
-.build-history .-build .-icon.-error {
- stroke: var(--color-red);
-}
-
-.build-history .-build .-icon.-success {
- stroke: var(--color-bg-darker);
-}
-
-.build-history-svg.-build-history-icon {
- fill: none;
- fill-rule: evenodd;
-}
-
-.build-history-svg.-build-history-icon.-pending {
- fill: var(--color-gray);
- stroke: var(--color-gray);
-}
-
-.build-history-svg.-build-history-icon.-running {
- stroke: var(--color-yellow);
-}
-
-.build-history-svg.-build-history-icon.-success {
- stroke: var(--color-green);
-}
-
-.build-history .-build .-icon.-failure .-path-1,
-.build-history .-build .-icon.-error .-path-1 {
- fill: var(--color-bg-darker);
-}
-
-.build-history .-build .-icon.-failure .-path-2,
-.build-history .-build .-icon.-error .-path-2 {
- fill: var(--color-red);
-}
-
-.build-history .-build .-icon.-success .-path-2 {
- fill: var(--color-green);
-}
-
-.build-history .-build .-icon.-running .-path-2 {
- fill: var(--color-yellow);
-}
-
-.build-history .-build .-icon.-running .-path-2 {
- fill: var(--color-yellow);
-}
-
-.build-history .-build .-icon.-failure .-path-3,
-.build-history .-build .-icon.-error .-path-3 {
- stroke: var(--color-bg-darker);
- stroke-width: 2;
- stroke-linecap: square;
-}
-
-.build-history .-build .-icon.-pending .-path-3 {
- stroke: var(--color-bg-darker);
- fill: var(--color-bg-darker);
-}
-
-.build-history .-build .-icon.-success .-path-3 {
- stroke: var(--color-bg-darker);
-}
-
-.build-history .-build .-icon.-running .-path-3 {
- stroke: var(--color-bg-darker);
-}
-
-.build-history .-build .-tooltip {
- position: absolute;
- // not sure what to here to display the tooltip on top!
- z-index: 9999;
- top: 125%;
-
- display: flex;
- visibility: hidden;
- flex-direction: column;
-
- width: 300px;
- padding: 0.2em 0;
-
- text-align: center;
-
- color: var(--color-text);
- border: solid 1px var(--color-gray);
- border-radius: 3px;
- background-color: var(--color-bg-darker);
-
- font-weight: 300;
-}
-
-.build-history .-build:hover .-tooltip {
- visibility: visible;
-}
-
-.build-history .-build:hover .-tooltip::after {
- position: absolute;
- bottom: 100%; /* At the top of the tooltip */
-
- margin-left: 0.5em;
-
- content: " ";
-
- border-width: 5px;
- border-style: solid;
- border-color: transparent transparent var(--color-gray) transparent;
-}
-
-.build-history .-build .-info {
- padding: 0.2em 0.6em;
-
- font-size: 14px;
-}
-
-.build-history .-build .-line {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
-}
-
-.build-history .-build .-line.-header {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
-}
-
-.build-history .-build .-line .-label {
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
-}
-
-.build-history .-build .-number {
- margin-right: 0.5em;
- &:before {
- content: "#";
- }
-}
-
-.build-history .-build .-event {
- font-style: italic;
-}
-
-.steps {
- position: relative;
-}
-
-.step {
- display: flex;
-}
-
-.step.-line {
- background: linear-gradient(
- 90deg,
- hsla(0, 0, 0, 0) calc(3.25em - 1px),
- hsla(0, 0%, 75%, 1) calc(3.25em),
- hsla(0, 0, 0, 0) calc(3.25em + 1px)
- );
-}
-.step.-line.-last {
- background-repeat: no-repeat;
- background-size: 100% 2em;
-}
-.step .-status {
- margin-top: 0.65em;
- margin-right: 1em;
- margin-left: 2.35em;
-}
-
-.step .-icon-container {
- padding-top: 12px;
- padding-bottom: 12px;
-
- background: var(--color-bg);
-}
-
-.step-status-icon {
- margin-top: 18px;
- margin-right: 36px;
- margin-left: 36px;
-}
-
-.step .-icon.-pending {
- padding: 8px;
-
- border: 2px solid var(--color-gray);
- border-radius: 7px;
-}
-
-.-step-icon.-pending {
- stroke: var(--color-gray);
- fill: var(--color-gray);
-}
-
-.-step-icon.-running {
- stroke: var(--color-yellow);
-}
-
-.-step-icon.-success {
- stroke: var(--color-green);
-}
-
-.-step-icon.-failure,
-.-step-icon.-error {
- stroke: var(--color-red);
-}
-
-.step .-view {
- flex: 1;
-
- margin: 1em 0;
- padding: 0em;
-
- border-top: var(--line-width) solid;
- border-left: var(--line-width) solid;
- border-image-source: linear-gradient(
- to right,
- var(--color-gray) 75%,
- transparent 75%,
- transparent 77%,
- var(--color-gray) 77%,
- var(--color-gray) 84%,
- transparent 84%
- );
- border-image-slice: 1;
-}
-
-.step .-view.-running {
- border-color: var(--color-yellow);
-}
-
-.step .summary {
- padding-top: 0.35em;
- padding-bottom: 0.35em;
-}
-
-.step .-info {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
-
- margin-left: 1.2em;
-}
-
-.step .-info .-duration {
- font-family: var(--font-code);
-}
-
-.loading-logs {
- padding: 0.5em 0;
- margin-left: 0.3em;
- margin-top: 0.3em;
-}
-
-.logs-container {
- background-color: var(--color-bg-darker);
- padding: 0.5em 0;
-}
-
-.logs {
- font-size: 14px;
- font-weight: 300;
-}
-
-.logs .line {
- margin: 0 0.5em;
- display: flex;
-}
-
-.logs .line .wrapper {
- padding-left: 1em;
- flex: 1;
- display: flex;
- flex-direction: row;
-}
-
-.logs .wrapper.-focus {
- background: var(--color-focus);
-}
-
-.logs .line .-line-num {
- margin-right: 1em;
-
- color: var(--color-offwhite);
-
- font-family: var(--font-code);
- > a {
- text-decoration: none;
- &:hover {
- text-decoration: underline;
- }
- }
-}
-
-.step-error {
- color: var(--color-scarlet);
- font-size: 14px;
- margin-left: 1.5em;
-}
-
-.step-error .message{
- margin-left: 0.2em;
-}
-
-.animated {
- animation-duration: 1s;
-
- animation-fill-mode: both;
-}
-
-.bounceInRight {
- animation-name: bounceInRight;
-}
-
-.fadeOutRightBig {
- animation-name: fadeOutRightBig;
-}
-
-.alerts {
- ol {
- display: flex;
- flex-direction: column-reverse;
- }
-
- li {
- flex: 0 0 auto;
- }
-}
-
-.alert-container-attributes {
- position: fixed;
- right: 0;
- bottom: 10px;
-
- width: 100%;
- max-width: 530px;
- margin: 0;
- padding: 0;
-
- list-style-type: none;
-}
-
-.alert-item-attributes {
- max-height: 100px;
- margin: 1em 1em 0 1em;
-
- transition: max-height 1.2s, margin-top 1.2s;
-}
-
-.alert-container {
- width: 500px;
- padding: 1em;
-
- cursor: pointer;
-
- color: white;
- border-radius: 5px;
- border-radius: 0px;
- background-color: var(--color-bg-darker);
- box-shadow: 0 5px 5px -5px hsla(0, 0%, 0%, 0.5);
-
- font-size: 14px;
-}
-
-.alert-container .-title {
- margin: 0;
-
- font-size: 1em;
-}
-
-.alert-container .-message {
- display: flex;
- overflow-y: auto;
- flex-direction: row;
- justify-content: space-between;
-
- max-height: 3.25em;
- margin-top: 0.25em;
- margin-bottom: 0;
-
- font-size: 0.9em;
-}
-
-.alert-container.-success {
- border: 1px solid var(--color-green);
-}
-
-.alert-container.-warning {
- border: 1px solid var(--color-yellow);
-}
-
-.alert-container.-error {
- border: 1px solid var(--color-red);
-}
-
-.hooks {
- width: 100%;
- padding-bottom: 0.5em;
-
- background: var(--color-darkcoal);
-}
-
-.hooks .loading {
- display: flex;
-
- margin-top: 0.5em;
- margin-left: 0.4em;
-}
-
-.hooks .row {
- display: flex;
- align-items: center;
- flex-direction: row;
-
- margin: 0.3em 0;
- > .hook-summary {
- position: relative;
-
- overflow: hidden;
-
- padding: 0;
-
- cursor: pointer;
-
- // hide details marker on Chrome
- &::-webkit-details-marker {
- display: none !important;
- }
-
- //hide details marker on Firefox
- &:first-of-type {
- list-style-type: none !important;
- }
- }
- .chevron {
- margin-right: 0.8em;
- margin-left: 1em;
-
- transition: all 0.2s;
- transform-origin: 50% 49%;
-
- color: var(--color-primary);
- }
- &[open] .chevron {
- transform: rotate(0.5turn);
- }
-}
-
-.hooks .row.preview {
- display: flex;
- align-items: center;
- flex-direction: row;
-}
-
-.hooks .headers {
- display: flex;
- align-items: center;
- flex-direction: row;
-
- margin-top: 0.3em;
- padding-top: 0.3em;
- padding-bottom: 0.3em;
-
- border-bottom: 1px solid var(--color-gray);
-}
-
-.hooks .header {
- flex: 1;
-
- text-align: center;
-
- font-size: 18px;
-}
-
-.hooks .row .preview {
- margin: 0.4em 0;
-
- font-size: 14px;
-}
-
-.hook-status.-no-fill {
- fill: none;
-}
-
-.hook-status.-success {
- stroke: var(--color-green);
- color: var(--color-green);
-}
-
-.hook-status.-failure {
- stroke: var(--color-scarlet);
- color: var(--color-scarlet);
-}
-
-.hooks .row .cell {
- align-items: center;
- flex: 1;
- justify-content: center;
-
- text-align: center;
-
- font-weight: 300;
-}
-
-.hooks .source-id {
- min-width: 330px;
-}
-
-.hooks .first-cell {
- width: 84px;
-}
-
-.hooks .row .cell.source-id {
- display: flex;
- flex-direction: row;
- justify-content: center;
-}
-
-.hooks .row .cell.source-id .text {
- padding: 3px 12px;
- flex: 1;
- text-align: center;
- color: var(--color-offwhite);
- background: var(--color-gray);
-}
-
-.preview .status.success {
- color: var(--color-green);
-}
-
-.preview .status.failure {
- color: var(--color-scarlet);
-}
-
-.hooks .row .info {
- display: flex;
- display: inline-block;
- flex-direction: row;
-
- width: 100%;
- padding: 0.4em 1em;
-
- border-bottom: 1px solid var(--color-gray);
- border-left: 1px solid var(--color-gray);
-
- font-size: 14px;
-}
-
-.hooks .row .info.-pending {
- border-left-color: var(--color-gray);
-}
-
-.hooks .row .info.-running {
- border-left-color: var(--color-yellow);
-}
-
-.hooks .row .info.-success {
- border-left-color: var(--color-green);
-}
-
-.hooks .row .info.-failure,
-.hooks .row .info.-error {
- border-left-color: var(--color-scarlet);
-}
-
-.hooks .row .info .element span.-m-l,
-.hooks .row .info .element .-m-l {
- margin-left: 0.4em;
-}
-
-.hooks .row .info .element span.-m-r,
-.hook-build .details .element .-m-r {
- margin-right: 0.4em;
-}
-
-.hooks .row .info .element .error-label {
- color: var(--color-scarlet);
-}
-
-.hooks .row .info .element .hook-build-status.-pending {
- color: var(--color-offwhite);
-}
-
-.hooks .row .info .element .hook-build-status.-running {
- color: var(--color-yellow);
-}
-
-.hooks .row .info .element .hook-build-status.-success {
- color: var(--color-green);
-}
-
-.hooks .row .info .element .hook-build-status.-failure,
-.hooks .row .info .element .hook-build-status.-error {
- color: var(--color-scarlet);
-}
-
-.repo-settings {
- display: flex;
- flex-direction: column;
-}
-
-.repo-settings .row {
- display: flex;
- flex-direction: row;
-}
-
-.repo-settings .category {
- width: 22em;
- margin-right: 2em;
-}
-
-.repo-settings .category .header {
- margin-bottom: 0.5em;
- padding-bottom: 0.2em;
-
- border-bottom: 2px solid var(--color-lavender);
-}
-
-.repo-settings .category .header .text {
- position: relative;
- top: 0.1em;
-
- font-size: 22px;
- font-weight: bold;
-}
-
-.repo-settings .category .description {
- font-size: 14px;
- font-weight: 300;
-}
-
-.inputs {
- margin: 1em;
-}
-
-.checkbox {
- margin-bottom: 0.5em;
-}
-
-.radio {
- display: flex;
- align-items: center;
-}
-
-.checkbox .label {
- font-size: 16px;
- font-weight: bold;
-}
-
-.checkbox .field-info {
- margin-left: 0.25em;
-
- font-weight: 300;
-}
-
-.inputs.repo-timeout input {
- width: 5em;
- padding-top: 0.5em;
- padding-right: 0em;
- padding-bottom: 0.5em;
- padding-left: 1em;
-
- text-align: center;
-
- color: white;
- border: none;
- border-bottom: 1px solid var(--color-lavender);
- background: none;
-
- font-size: 14px;
-}
-
-.repo-timeout input:focus {
- border-color: var(--color-cyan);
-}
-
-.repo-timeout {
- margin-top: 1em;
- margin-left: 3em;
-}
-
-.repo-timeout .label {
- margin-left: 0.75em;
-
- font-size: 16px;
- font-weight: 300;
-}
-
-.repo-timeout input:invalid {
- color: var(--color-red);
-
- caret-color: var(--color-offwhite);
-}
-
-.timeout-help {
- padding: 8px 12px;
-
- border-radius: 4px;
- background: var(--color-gray);
-
- font-size: 14px;
- font-weight: 300;
-}
-
-.checkbox input[type="checkbox"],
-.checkbox input[type="radio"] {
- position: relative;
- right: 1.55em;
- bottom: 0.1em;
-
- opacity: 0;
-}
-
-.checkbox label {
- position: relative;
-}
-
-.checkbox label::before,
-.checkbox label::after {
- position: absolute;
-}
-
-.checkbox label::before {
- top: calc(50% - 0.45em);
- left: -1.875em;
-
- width: 1.15em;
- height: 1.15em;
-
- content: "";
-}
-
-.checkbox.radio label::before {
- top: calc(50% - 0.575em);
- left: -1.8em;
-
- width: 1.15em;
- height: 1.15em;
-
- content: "";
-}
-
-.checkbox input[type="checkbox"] + label::after,
-.checkbox input[type="radio"] + label::after {
- content: none;
-}
-
-.checkbox input[type="checkbox"]:focus + label::before,
-.checkbox input[type="radio"]:focus + label::before {
- outline: var(--color-cyan) auto 5px;
-}
-
-.pager-actions {
- display: flex;
- justify-content: space-between;
-}
+@import "animations";
+@import "main";
+@import "themes";
diff --git a/src/static/index.ts b/src/static/index.ts
index 54279c6ef..6be807d5d 100644
--- a/src/static/index.ts
+++ b/src/static/index.ts
@@ -2,7 +2,7 @@
//
// Use of this source code is governed by the LICENSE file in this repository.
-import { Elm, Flags, App, Config, Session } from "../elm/Main";
+import { Elm, Flags, App, Config, Session, Theme } from "../elm/Main";
import "../scss/style.scss";
// Vela consts
@@ -16,6 +16,13 @@ const currentSessionState: Session | null = storedSessionState
? JSON.parse(storedSessionState)
: null;
+// setup for stored theme
+const themeKey: string = "vela-theme";
+const defaultTheme: string = "theme-dark";
+const storedThemeState: string | null = localStorage.getItem(themeKey);
+const currentThemeState: Theme =
+ (storedThemeState as Theme) || (defaultTheme as Theme);
+
// Vela flags; configuration for bootstrapping Vela Elm UI
const flags: Flags = {
isDev: process.env.NODE_ENV === "development",
@@ -30,7 +37,8 @@ const flags: Flags = {
process.env.VELA_DOCS_URL ||
envOrNull("VELA_DOCS_URL", "$VELA_DOCS_URL") ||
docsURL,
- velaSession: currentSessionState || null
+ velaSession: currentSessionState || null,
+ velaTheme: currentThemeState || (defaultTheme as Theme)
};
// create the configuration object for Elm
@@ -53,6 +61,18 @@ app.ports.storeSession.subscribe(sessionMessage => {
setTimeout(() => app.ports.onSessionChange.send(sessionMessage), 0);
});
+app.ports.setTheme.subscribe(theme => {
+ let body: HTMLElement = document.getElementsByTagName("body")[0];
+
+ if (!body.classList.contains(theme)) {
+ body.className = "";
+ body.classList.add(theme);
+ }
+
+ localStorage.setItem(themeKey, theme);
+ setTimeout(() => app.ports.onThemeChange.send(theme), 0);
+});
+
/**
* envOrNull is a basic helper that returns a substituted
* environment variable or null