From 6ecbdff51fda5653f375324609f8e9dc8861b7f3 Mon Sep 17 00:00:00 2001 From: Meis Date: Thu, 22 Jun 2023 09:50:44 -0600 Subject: [PATCH] WIP: Footer --- src/components/Footer/Footer.less | 198 ++++++++++++++++++ src/components/Footer/Footer.stories.tsx | 15 ++ src/components/Footer/Footer.tsx | 249 +++++++++++++++++++++++ src/components/Footer/SocialMedia.less | 102 ++++++++++ 4 files changed, 564 insertions(+) create mode 100644 src/components/Footer/Footer.less create mode 100644 src/components/Footer/Footer.stories.tsx create mode 100644 src/components/Footer/Footer.tsx create mode 100644 src/components/Footer/SocialMedia.less diff --git a/src/components/Footer/Footer.less b/src/components/Footer/Footer.less new file mode 100644 index 00000000..8863af0f --- /dev/null +++ b/src/components/Footer/Footer.less @@ -0,0 +1,198 @@ +@import (reference) url('@cfpb/cfpb-design-system/src/cfpb-design-system.less'); + +// https://github.com/cfpb/consumerfinance.gov/blob/main/cfgov/unprocessed/css/organisms/footer.less + +/* ========================================================================== + consumerfinance.gov + footer + ========================================================================== */ + +/* topdoc + name: CF.gov site-wide footer. + family: cfgov-organisms + patterns: + - notes: + - "For the markup please see _layouts/organisms/footer.html." + tags: + - cfgov-organisms +*/ + +.o-footer { + // Adding an extra 5px to the top to account for the absolute positioned + // social media icons. + padding-top: unit((50px / @base-font-size-px), em); + // There is a 10px margin-bottom on the last .o-footer_list li's, plus the + // 50px bottom padding = 60px of total padding at the bottom of the footer. + padding-bottom: unit((50px / @base-font-size-px), em); + border-top: 5px solid @green; + background: @gray-5; + + &_nav-list { + .m-list__links(); + + .m-list_link { + font-size: unit((18px / @base-font-size-px), em); + .u-link__colors( @black ); + + .respond-to-min( @bp-sm-min, { + margin-right: 1em; + + .u-link__hover-border(); + } ); + + .respond-to-min( @bp-med-min, { + margin-right: unit(( @grid_gutter-width / 22px), em ); + font-size: unit( (20px / @base-font-size-px), em ); + } ); + } + + .m-list_link.m-list_link__disabled { + border-bottom: 1px dotted; + + .respond-to-min( @bp-med-min, { + .u-link__no-border(); + } ); + } + } + + &_list { + .m-list__links(); + + .m-list_link { + .u-link__colors( @gray-dark ); + } + } + + &_pre { + position: relative; + margin-bottom: unit((45px / @base-font-size-px), em); + + .o-footer_top-button { + display: block; + width: 100%; + text-align: center; + // There's a standard margin between the top + // of the footer and the links. The button comes between + // that margin in the wireframes. + margin: -2em auto 2em; + } + + .o-footer_nav-list { + margin-bottom: 0; + } + + .respond-to-min( @bp-sm-min, { + padding-bottom: unit( (@grid_gutter-width / @base-font-size-px), em ); + margin-bottom: unit( (@grid_gutter-width / @base-font-size-px), em ); + border-bottom: 1px solid @gray-40; + + .o-footer_top-button { + display: none; + } + + .o-footer_nav-list { + .m-list__horizontal(); + + .m-list_item { + margin-bottom: 0; + } + } + } ); + + .respond-to-print( { + // !important used here to avoid being overriden by a much more specific + // selector that sets the display property for this element + // and to avoid using a selector that specific here. + display: none !important; + } ); + } + + // TODO: Refactor to use Design System Layout package. + &-middle-left { + .o-footer_list { + margin: 0; + } + + /* Fix doubled border in mobile view */ + .respond-to-max( @bp-xs-max, { + .o-footer_col:nth-child( n+2 ) { + .o-footer_list { + .m-list_item .m-list_link { + border-top-width: 0; + } + } + } + } ); + + .respond-to-min( @bp-sm-min, { + .grid_column(8); + border-right: 1px solid @gray-40; + border-left: 0; + + .o-footer_col { + .grid_column(6); + border-left: 0; + border-right: 0; + padding-right: unit( (@grid_gutter-width / 2 / @base-font-size-px), em ); + } + } ); + + .respond-to-print( { + // !important used here to avoid being overriden by a much more specific + // selector that sets the display property for this element + // and to avoid using a selector that specific here. + display: none !important; + } ); + } + + &-middle-right { + /* Fix doubled border in mobile view */ + .respond-to-max( @bp-xs-max, { + .o-footer_list { + .m-list_item .m-list_link { + border-top-width: 0; + } + } + } ); + + .respond-to-min( @bp-sm-min, { + .grid_column(4); + + .o-footer_list { + padding-left: @grid_gutter-width; + padding-right: @grid_gutter-width; + } + } ); + + .respond-to-print( { + // !important used here to avoid being overriden by a much more specific + // selector that sets the display property for this element + // and to avoid using a selector that specific here. + display: none !important; + } ); + } + + &-post { + margin-top: unit((@grid_gutter-width / @base-font-size-px), em); + + .respond-to-min( @bp-sm-min, { + padding-top: unit( (@grid_gutter-width / @base-font-size-px), em ); + border-top: 1px solid @gray-40; + } ); + + .respond-to-print( { + padding: 0; + border: none; + margin: 0; + } ); + } +} + +/* topdoc + name: EOF + eof: true +*/ + +.o-footer .cf-icon-svg__external-link { + margin-left: 3px; +} \ No newline at end of file diff --git a/src/components/Footer/Footer.stories.tsx b/src/components/Footer/Footer.stories.tsx new file mode 100644 index 00000000..119673cb --- /dev/null +++ b/src/components/Footer/Footer.stories.tsx @@ -0,0 +1,15 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import Footer from './Footer'; + +const meta: Meta = { + component: Footer, + argTypes: {} +}; + +export default meta; + +type Story = StoryObj; + +export const CFGov: Story = { + args: {} +}; diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx new file mode 100644 index 00000000..102b81b2 --- /dev/null +++ b/src/components/Footer/Footer.tsx @@ -0,0 +1,249 @@ +import React from 'react'; +import { Icon } from '../Icon/Icon'; +import './Footer.less'; +import './SocialMedia.less'; + +const BackToTop = (): JSX.Element => ( + + Back to top + + +); + +interface NavLinksProperties { + children: JSX.Element[]; +} +const NavLinks = ({ children }: NavLinksProperties): JSX.Element => ( +
    + {children.map(link => ( +
  • + {React.cloneElement(link, { + className: `m-list_link ${link.props?.className ?? ''}` + })} +
  • + ))} +
+); + +const FooterBanner = (): JSX.Element => ( +
+
+ +
+ An official website of the  + United States government +
+
+
+); + +export default function Footer(): JSX.Element { + return ( + + ); +} diff --git a/src/components/Footer/SocialMedia.less b/src/components/Footer/SocialMedia.less new file mode 100644 index 00000000..dac535ae --- /dev/null +++ b/src/components/Footer/SocialMedia.less @@ -0,0 +1,102 @@ +@import (reference) url('@cfpb/cfpb-design-system/src/cfpb-design-system.less'); + +// https://github.com/cfpb/consumerfinance.gov/blob/main/cfgov/unprocessed/css/molecules/social-media.less + +/* topdoc + name: Social Media + family: cfgov-molecules + patterns: + - name: Default example + markup: | + + codenotes: + - | + Structural cheat sheet: + ----------------------- + .m-social-media.m-social-media__share + .m-social-media_heading + ul.m-list + li.m-list_item + .m-social-media_icon + .cf-icon-svg + span.u-visually-hidden + notes: + - "m-social-media__follow modifier is identical + but does not include the heading." + tags: + - cfgov-molecules +*/ + +.m-social-media { + display: inline-block; + + // This could be wrapped in the `m-social-media__share` modifier, + // but it's ok on its own too. + &_heading { + .heading-5(); + display: inline-block; + // 0.25em subtracted to compensate for inline spacing + padding-right: unit((@grid_gutter-width / @font-size - 0.25em), em); + margin-bottom: 0; + vertical-align: middle; + } + + .m-list { + display: inline-block; + vertical-align: middle; + + .m-list_item { + // 0.25em subtracted to compensate for inline spacing + margin-right: unit( + ((@grid_gutter-width / 2) / @base-font-size-px - 0.25em), + em + ); + margin-bottom: 0; + } + + .m-list_item:last-child { + margin-right: 0; + } + } + + &_icon { + color: @gray-dark; + font-size: unit((@grid_gutter-width / @base-font-size-px), em); + line-height: 1; + .u-link__colors( @gray-dark, @pacific-80 ); + .u-link__no-border(); + } + + &_print { + padding-left: unit(((@grid_gutter-width / 2) / @base-font-size-px), em); + border-left: 1px solid @black; + } + + // Hide on print. + .respond-to-print( { + & { + display: none; + } + } ); +} + +.no-js .m-social-media_print { + display: none; +} + +/* topdoc + name: EOF + eof: true +*/