From 71c0c0e95ae9c508492dff5b802156e062c19872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedikt=20R=C3=B6tsch?= Date: Mon, 2 Oct 2017 17:33:40 +0200 Subject: [PATCH] course detail styling and basic lesson module styling --- assets/stylesheets/course/index.css | 64 +++++++ .../stylesheets/global/table-of-contents.css | 65 +++---- assets/stylesheets/layout/layout-sidebar.css | 7 +- assets/stylesheets/lesson/index.css | 16 +- public/images/icon-duration.svg | 7 + public/images/icon-skill-level.svg | 6 + public/stylesheets/style.css | 163 +++++++++++++++--- routes/courses.js | 9 +- views/course.pug | 45 +++-- views/mixins/_lesson.pug | 42 +++-- views/mixins/_lessonModuleCodeSnippet.pug | 1 - views/mixins/_lessonModuleCopy.pug | 1 - views/mixins/_lessonModuleImage.pug | 1 - 13 files changed, 335 insertions(+), 92 deletions(-) create mode 100644 public/images/icon-duration.svg create mode 100644 public/images/icon-skill-level.svg diff --git a/assets/stylesheets/course/index.css b/assets/stylesheets/course/index.css index 473a258..7e8c2c3 100644 --- a/assets/stylesheets/course/index.css +++ b/assets/stylesheets/course/index.css @@ -1 +1,65 @@ @import './card'; + +@block course { + display: flex; + flex-direction: column; + justify-content: space-between; + + @element title { + margin-bottom: calc(var(--grid-gutter)); + } + + @element overview { + font-family: var(--font-medium); + + @media (--breakpoint-desktop) { + float: right; + border-radius: var(--border-radius); + box-shadow: var(--card-box-shadow); + width: 228px; + margin: 0 0 var(--grid-gutter) var(--grid-gutter); + } + } + + @element overview-title { + border-bottom: 1px solid var(--color-sidebar-seperator); + padding: calc(var(--grid-gutter) / 2) 0; + margin: 0; + line-height: 1.31; + font-weight: normal; + text-transform: uppercase; + text-align: center; + } + + @element overview-item { + display: flex; + align-items: center; + padding: calc(var(--grid-gutter) / 2); + border-bottom: 1px solid var(--color-sidebar-seperator); + + line-height: 1.54; + font-size: 0.8em; + } + + @element overview-icon { + flex: 0 0 auto; + width: 24px; + height: 24px; + padding-right: calc(var(--grid-gutter) / 2); + } + + @element overview-value { + flex: 1 0 auto; + } + + @element overview-cta-wrapper { + padding: calc(var(--grid-gutter) / 2) 0; + text-align: center; + } + @element overview-cta { + margin: 0; + } + @element cta { + margin-bottom: 0; + } +} diff --git a/assets/stylesheets/global/table-of-contents.css b/assets/stylesheets/global/table-of-contents.css index 87822bd..7ca8088 100644 --- a/assets/stylesheets/global/table-of-contents.css +++ b/assets/stylesheets/global/table-of-contents.css @@ -1,38 +1,45 @@ -.table-of-contents { - & ul { +:root { + --toc-gap-left: calc(var(--grid-gutter) / 2) +} +@block table-of-contents { + @element list { list-style: none; - margin: var(--grid-gutter) 0; + margin: 0; padding: 0; - padding-left: calc(var(--grid-gutter) * 2); + padding-left: var(--toc-gap-left); + } - & li { - margin: 0 0 var(--grid-gutter); - padding: 0; - } + @element item { + margin: 0 0 calc(var(--grid-gutter) / 4); + padding: 0; + font-size: 0.9em; + line-height: 1.8; + } - & a { - display: block; + @element link { + display: block; + color: var(--color-text-grey); - &.active { - position: relative; - font-weight: bold; + &.active { + position: relative; + font-family: var(--font-medium); + color: var(--color-text-default); - &:before, - &:after { - position: absolute; - top: 0; - } - &:before { - content: ''; - left: calc(var(--grid-gutter) * 3 * -1); - bottom: 0; - width: 3px; - background: var(--color-course-active); - } - &:after { - content: '👁'; - left: calc(var(--grid-gutter) * 2 * -1); - } + &:before, + &:after { + position: absolute; + top: 0; + } + &:before { + content: ''; + left: calc(var(--toc-gap-left) * 3 * -1); + bottom: 0; + width: 3px; + background: var(--color-course-active); + } + &:after { + content: '👁'; + left: calc(var(--toc-gap-left) * 2 * -1); } } } diff --git a/assets/stylesheets/layout/layout-sidebar.css b/assets/stylesheets/layout/layout-sidebar.css index 4781d98..619a21a 100644 --- a/assets/stylesheets/layout/layout-sidebar.css +++ b/assets/stylesheets/layout/layout-sidebar.css @@ -18,7 +18,7 @@ } @element sidebar-header { - padding: 1em var(--grid-gutter); + padding: calc(var(--grid-gutter) / 2) var(--grid-gutter); border-bottom: 1px solid var(--color-sidebar-seperator); } @@ -35,7 +35,10 @@ } @element content { - padding: var(--grid-gutter) 0; + display: flex; + flex-direction: column; + align-self: stretch; + padding-top: calc(var(--grid-gutter) / 2); @media (--breakpoint-desktop) { flex: 0 1 auto; diff --git a/assets/stylesheets/lesson/index.css b/assets/stylesheets/lesson/index.css index 80e9fb9..5cadc5a 100644 --- a/assets/stylesheets/lesson/index.css +++ b/assets/stylesheets/lesson/index.css @@ -1,8 +1,22 @@ .lesson-module { - margin-top: calc(var(--grid-gutter) * 3); + margin-top: var(--grid-gutter); & img { width: 100%; height: auto; } } + +@block lesson { + display: flex; + flex-direction: column; + justify-content: space-between; + + @element title { + margin-bottom: calc(var(--grid-gutter)); + } + + @element cta { + margin-bottom: 0; + } +} diff --git a/public/images/icon-duration.svg b/public/images/icon-duration.svg new file mode 100644 index 0000000..fa1cf23 --- /dev/null +++ b/public/images/icon-duration.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/icon-skill-level.svg b/public/images/icon-skill-level.svg new file mode 100644 index 0000000..a12fbe0 --- /dev/null +++ b/public/images/icon-skill-level.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 6b20ae8..288da96 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -793,7 +793,7 @@ input[type="reset"]:focus, } .layout-sidebar__sidebar-header { - padding: 1em 22px; + padding: 11px 22px; border-bottom: 1px solid #eeeeee; } @@ -810,7 +810,16 @@ input[type="reset"]:focus, } .layout-sidebar__content { - padding: 22px 0; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-item-align: stretch; + align-self: stretch; + padding-top: 11px; } @media (min-width: 700px) { @@ -1138,44 +1147,50 @@ display: flex; height: auto; } -.table-of-contents ul { - list-style: none; - margin: 22px 0; - padding: 0; - padding-left: 44px; +.table-of-contents {} + +.table-of-contents__list { + list-style: none; + margin: 0; + padding: 0; + padding-left: 11px; } -.table-of-contents ul li { - margin: 0 0 22px; - padding: 0; +.table-of-contents__item { + margin: 0 0 5.5px; + padding: 0; + font-size: 0.9em; + line-height: 1.8; } -.table-of-contents ul a { - display: block; +.table-of-contents__link { + display: block; + color: #8091a5; } -.table-of-contents ul a.active { +.table-of-contents__link.active { position: relative; - font-weight: bold; + font-family: 'robotomedium', Helvetica, sans-serif; + color: #2a3039; } -.table-of-contents ul a.active:before, - .table-of-contents ul a.active:after { +.table-of-contents__link.active:before, + .table-of-contents__link.active:after { position: absolute; top: 0; } -.table-of-contents ul a.active:before { +.table-of-contents__link.active:before { content: ''; - left: -66px; + left: -33px; bottom: 0; width: 3px; background: #536171; } -.table-of-contents ul a.active:after { +.table-of-contents__link.active:after { content: '👁'; - left: -44px; + left: -22px; } .sidebar-menu {} @@ -1475,6 +1490,91 @@ github.com style (c) Vasily Polovnyov letter-spacing: 2px; } +.course { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.course__title { + margin-bottom: 22px; +} + +.course__overview { + font-family: 'robotomedium', Helvetica, sans-serif; +} + +@media (min-width: 700px) { + + .course__overview { + float: right; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px 1px rgba(0, 0, 0, .1); + box-shadow: 0 1px 3px 1px rgba(0, 0, 0, .1); + width: 228px; + margin: 0 0 22px 22px; + } +} + +.course__overview-title { + border-bottom: 1px solid #eeeeee; + padding: 11px 0; + margin: 0; + line-height: 1.31; + font-weight: normal; + text-transform: uppercase; + text-align: center; +} + +.course__overview-item { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 11px; + border-bottom: 1px solid #eeeeee; + + line-height: 1.54; + font-size: 0.8em; +} + +.course__overview-icon { + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: 24px; + height: 24px; + padding-right: 11px; +} + +.course__overview-value { + -webkit-box-flex: 1; + -ms-flex: 1 0 auto; + flex: 1 0 auto; +} + +.course__overview-cta-wrapper { + padding: 11px 0; + text-align: center; +} + +.course__overview-cta { + margin: 0; +} + +.course__cta { + margin-bottom: 0; +} + .module-hero-image {} .module-hero-image__wrapper { @@ -1646,10 +1746,31 @@ github.com style (c) Vasily Polovnyov } .lesson-module { - margin-top: 66px + margin-top: 22px } .lesson-module img { width: 100%; height: auto; } + +.lesson { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.lesson__title { + margin-bottom: 22px; +} + +.lesson__cta { + margin-bottom: 0; +} diff --git a/routes/courses.js b/routes/courses.js index 6141015..3e92480 100644 --- a/routes/courses.js +++ b/routes/courses.js @@ -48,7 +48,14 @@ router.get('/:cslug/lessons/:lslug', async function (req, res, next) { const lessons = course.fields.lessons const lessonIndex = lessons.findIndex((lesson) => lesson.fields.slug === req.params.lslug) const lesson = lessons[lessonIndex] - res.render('course', {title: `${course.fields.title} | ${lesson.fields.title}`, course, lesson, lessons, lessonIndex}) + const nextLesson = lessons[lessonIndex + 1] || null + res.render('course', { + title: `${course.fields.title} | ${lesson.fields.title}`, + course, + lesson, + lessons, + nextLesson + }) }) module.exports = router diff --git a/views/course.pug b/views/course.pug index 31e9bc7..517c2ad 100644 --- a/views/course.pug +++ b/views/course.pug @@ -7,22 +7,35 @@ block content section.layout-sidebar__sidebar .layout-sidebar__sidebar-header h2.layout-sidebar__sidebar-title Table of contents - .table-of-contents - ul - li - a.active(href=`/courses/${course.fields.slug}`) Course overview - each l in course.fields.lessons - if l.fields - li - a(href=`/courses/${course.fields.slug}/lessons/${l.fields.slug}${queryString}`) #{l.fields.title} + .layout-sidebar__sidebar-content + .table-of-contents + .table-of-contents__list + .table-of-contents__item + a.table-of-contents__link.active(href=`/courses/${course.fields.slug}`) Course overview + each l in course.fields.lessons + if l.fields + .table-of-contents__item + a.table-of-contents__link(href=`/courses/${course.fields.slug}/lessons/${l.fields.slug}${queryString}`) #{l.fields.title} section.layout-sidebar__content - h1= course.fields.title - if lesson - +lesson(lesson) - if lessonIndex + 1< lessons.length - if lessons[lessonIndex + 1].fields - a.cta(href=`/courses/${course.fields.slug}/lessons/${lessons[lessonIndex + 1].fields.slug}${queryString}`) View next lesson + +lesson(lesson, course, nextLesson) else - p !{helpers.markdown(course.fields.description)} - a.cta(href=`/courses/${course.fields.slug}/lessons/${course.fields.lessons[0].fields.slug}${queryString}`) Start course + .course + .course__content + h1.course__title= course.fields.title + .course__overview + h3.course__overview-title Overview + if course.fields.duration + .course__overview-item + img.course__overview-icon(src='/images/icon-duration.svg') + .course__overview-value Duration: #{course.fields.duration}min + if course.fields.skillLevel + .course__overview-item + img.course__overview-icon(src='/images/icon-skill-level.svg') + .course__overview-value Skill level: #{course.fields.skillLevel} + .course__overview-cta-wrapper + a.course__overview-cta.cta(href=`/courses/${course.fields.slug}/lessons/${course.fields.lessons[0].fields.slug}${queryString}`) Start course + .course__description !{helpers.markdown(course.fields.description)} + .course__footer + a.course__cta.cta(href=`/courses/${course.fields.slug}/lessons/${course.fields.lessons[0].fields.slug}${queryString}`) Start course + //pre= helpers.dump(course) diff --git a/views/mixins/_lesson.pug b/views/mixins/_lesson.pug index 8b660a1..649928c 100644 --- a/views/mixins/_lesson.pug +++ b/views/mixins/_lesson.pug @@ -2,23 +2,27 @@ include _lessonModuleCodeSnippet include _lessonModuleCopy include _lessonModuleImage -mixin lesson(lesson) +mixin lesson(lesson, course, nextLesson) .lesson - h2.lesson__tilte #{lesson.fields.title} - div.lesson__short-description !{helpers.markdown(lesson.fields.description)} - if lesson.fields.image - img.lesson__image(src=`${lesson.fields.image.fields.file.url}` alt=`${lesson.fields.image.fields.title}`) - each module in lesson.fields.modules - if module.sys.contentType - case module.sys.contentType.sys.id - when 'lessonModuleCodeSnippets' - +lessonModuleCodeSnippet(module) - when 'lessonModuleCopy' - +lessonModuleCopy(module) - when 'lessonModuleImage' - +lessonModuleImage(module) - else - h2 ️️⚠️ Invalid lesson module - p - span Could not determine type of - strong #{module.sys.id} + .lesson_content + h1.lesson__title #{lesson.fields.title} + div.lesson__short-description !{helpers.markdown(lesson.fields.description)} + if lesson.fields.image + img.lesson__image(src=`${lesson.fields.image.fields.file.url}` alt=`${lesson.fields.image.fields.title}`) + each module in lesson.fields.modules + if module.sys.contentType + case module.sys.contentType.sys.id + when 'lessonModuleCodeSnippets' + +lessonModuleCodeSnippet(module) + when 'lessonModuleCopy' + +lessonModuleCopy(module) + when 'lessonModuleImage' + +lessonModuleImage(module) + else + h2 ️️⚠️ Invalid lesson module + p + span Could not determine type of + strong #{module.sys.id} + .lesson_footer + if nextLesson + a.lesson__cta.cta(href=`/courses/${course.fields.slug}/lessons/${nextLesson.fields.slug}${queryString}`) View next lesson diff --git a/views/mixins/_lessonModuleCodeSnippet.pug b/views/mixins/_lessonModuleCodeSnippet.pug index 75a8e9c..ab71713 100644 --- a/views/mixins/_lessonModuleCodeSnippet.pug +++ b/views/mixins/_lessonModuleCodeSnippet.pug @@ -1,6 +1,5 @@ mixin lessonModuleCodeSnippet(module) .lesson-module.lesson-module-code - h1.lesson-module__title #{module.fields.title} if module.fields.curl pre.lesson-module-code__curl code.shell= module.fields.curl diff --git a/views/mixins/_lessonModuleCopy.pug b/views/mixins/_lessonModuleCopy.pug index 4b41ab3..717cfe0 100644 --- a/views/mixins/_lessonModuleCopy.pug +++ b/views/mixins/_lessonModuleCopy.pug @@ -1,4 +1,3 @@ mixin lessonModuleCopy(module) .lesson-module.lesson-module-copy - h3.lesson-module-copy__title #{module.fields.title} .lesson-module-copy__copy !{helpers.markdown(module.fields.copy)} diff --git a/views/mixins/_lessonModuleImage.pug b/views/mixins/_lessonModuleImage.pug index 4c176bd..f14daa3 100644 --- a/views/mixins/_lessonModuleImage.pug +++ b/views/mixins/_lessonModuleImage.pug @@ -1,6 +1,5 @@ mixin lessonModuleImage(module) .lesson-module.lesson-module-image - h2.lesson-module-image__title #{module.fields.title} if module.fields.file && module.fields.file.url img.lesson-module-image__image(src=module.fields.file.url alt=module.fields.title) else