Browse Source

building I think\?

image-sizing
Logan McGrath 11 months ago
parent
commit
c874e09279
  1. 164
      about-me.html
  2. 4
      contact.md
  3. 53
      css/_default.sass
  4. 138
      css/_media.sass
  5. 25
      css/_theme.sass
  6. 5
      css/components/_code.sass
  7. 24
      css/components/_figures.sass
  8. 27
      css/components/_headers.sass
  9. 136
      css/media/_config.sass
  10. 31
      css/media/helpers/_logger.sass
  11. 27
      css/media/helpers/_no-media.sass
  12. 100
      css/media/helpers/_parser.sass
  13. 19
      css/media/helpers/_slice.sass
  14. 80
      css/media/helpers/_to-number.sass
  15. 41
      css/media/plugins/_tweakpoints.sass
  16. 43
      css/pages/_about-me.sass
  17. 2
      hie.yaml
  18. BIN
      images/about-me/headshot-w-meatball-doge_768w.png
  19. 4
      partials/employment-opportunities.html
  20. 19
      src/Site/Compiler/CustomPandoc.hs
  21. 9
      src/Site/Rule/Sass.hs

164
about-me.html

@ -7,40 +7,72 @@ sharing: true
footer: true
---
<section class="intro">
<div class="doge">
<figure>
<picture>
<source srcset='$route-to("images/about-me/headshot-w-meatball-doge_400w.png")$'>
<img width="200" height="266" src='$route-to("images/about-me/headshot-w-meatball-doge_400w.png")$'>
</picture>
<figcaption>
Myself and Meatball, who is a very good boy.
</figcaption>
</figure>
<section role="grid-container" class="intro">
<div class="meatball-headshot">
<div role="figure">
<figure>
<picture>
<source srcset='$route-to("images/about-me/headshot-w-meatball-doge_400w.png")$'>
<img width="288" height="384" src='$route-to("images/about-me/headshot-w-meatball-doge_400w.png")$'>
</picture>
<figcaption>
Myself and Meatball, who is a very good boy.
</figcaption>
</figure>
</div>
</div>
<div class="me">
<div class="meatball-jellybean">
<div role="figure">
<figure>
<picture>
<source srcset='$route-to("images/about-me/meatball-n-jelly-doges_400w.png")$'>
<img width="200" height="266" src='$route-to("images/about-me/meatball-n-jelly-doges_400w.png")$'>
</picture>
<figcaption>
The little one is Meatball, the big one is Jellybean. Both are very good boys. <em>Photo obtained in abundance of treats.</em>
</figcaption>
</figure>
</div>
</div>
<div class="myself">
<p>
Hello! I'm Logan McGrath, I'm a senior software engineer, and I specialize in a
segment of software applications engineering that sits between the framework and
the business logic. In my day job at <a href="https://creditkarma.com">Credit Karma</a>
I work with a team to provide building blocks with which my fellow product
engineers can more quickly deliver valuable features to members. By night I dabble
in esoteric programming languages, theories, and tools to better hone my craft. It's
what I like to do when my husband isn't encouraging me to go play outside.
Hello! I'm Logan McGrath, I'm a senior software engineer, and I specialize in a segment of software applications engineering that sits between the framework and the business logic. In my day job at <a href="https://creditkarma.com">Credit Karma</a> I work with a team to provide building blocks with which my fellow product engineers can more quickly deliver valuable features to members. By night I dabble in esoteric programming languages, theories, and tools to better hone my craft. It's what I like to do when my husband isn't encouraging me to go play outside.
</p>
<p>
I have been working in tech since 2007, where I started as just a wee web developer
putting together shopping carts. I've since found my way into heavier engineering
work.
I have been working in tech since 2007, where I started as just a wee web developer putting together shopping carts. I've since found my way into bigger projects.
</p>
</div>
</section>
<h2>My Professional Toolbox</h2>
<div class="life-itself">
<h2>My Life Away from Work</h2>
<p>
Every few months I try looping some electronic music, but I'm really bad at
it and I intend one day to share and self-deprecate.
</p>
<p>
When I'm away from the computer I'm spending time with my husband Dr. Corey Blanchette and our two dogs, Jellybean and Meatball. Corey cooks and I do the dishes if I can beat him to them. Most evenings we can be found on the patio enjoying my latest mixology attempt while he tries to teach me ways to remember different parts of human anatomy. For the most part all I can retain are the acronyms that sound inappropriate, and I'm happy to share them over a drink. One of my favorites is a mnemonic he was taught in school to name the "carpals", which I'm pretty sure is a kind of bone.
</p>
<p>
Sometimes I make bad drinks, but fortunately we have a reliable backup. We call it <em>The Bunny</em>, consisting of your choice of liquor, ginger beer, carrot juice, fresh lime, and a little bit of orange liqueur over the top. Adjust to taste and best served in a copper mug. Very refreshing, and it might even count as a serving of vegetables!
</p>
</div>
<div class="life-on-site">
<h2>My Life as It Relates To My Site</h2>
<p>
There is a large intersection between my personal and professional experiences. Many engineers post about their lives in their technical blogs, sometimes sharing deeply personal and difficult stories. I have the deepest of respect for their bravery and great appreciation for the empathy their writing instills. Though there are many events that have bled across my boundaries with work, I feel much trepidation sharing how I feel and describing how I process the world around me. I hope to share what is constructive, and that I can contribute to others' validation of their life experience as others have for myself.
</p>
<p>
All opinions I express here are my own. I m
</p>
</div>
</section>
<div class="professional-toolbox">
<div class="tools">
<section class="professional-offering">
<h2>My Professional Offering</h2>
<div class="professional-toolbox">
<section class="foundations">
<h3>Foundations</h3>
<ul>
@ -59,11 +91,10 @@ footer: true
<li>Scala</li>
<li>Ruby</li>
<li>Python</li>
<li>Java</li>
<li>C#</li>
<li>Rust</li>
<li>Java <em>and</em> C#</li>
<li>Haskell</li>
<li>TypeScript</li>
<li>JavaScript</li>
<li>(Type|Java)Script</li>
</ul>
</section>
<section class="databases">
@ -97,78 +128,11 @@ footer: true
</section>
</div>
<section>
<div role="grid-container" class="professionally-applied">
<h3>Tying It All Together</h3>
<p>
Product teams need solid building blocks to deliver features. I work as a
force multiplier to provide the low code tools that businesses require for
quicker implementation of features and product experiences for their
customers. I provide solutions that are stable and highly usable by product
engineers, and I take great pride in high quality work that delivers value.
Product teams need solid building blocks to deliver features. I work as a force multiplier to provide the low code tools that businesses require for quicker implementation of features and product experiences for their customers. I provide solutions that are stable and highly usable by product engineers, and I take great pride in high quality work that delivers value.
</p>
</section>
<section>
<h3>Looking for an Engineer?</h3>
<p>
For prospective employers, referrals, and recruiters: <em>I am happily
employed</em>, though I welcome chats about prospects if you believe I am
a good fit for your technical needs. I apply what works from my experience
to the team context and reinforce what the team is already doing well.
Sometimes my existing toolbelt doesn't work so well, and that's ok.
Together we'll find a way to make things better.
</p>
</section>
</div>
<section>
<h2>My Life Away from Work</h2>
<div class="doge">
<figure>
<picture>
<source srcset='$route-to("images/about-me/meatball-n-jelly-doges_400w.png")$'>
<img width="200" height="266" src='$route-to("images/about-me/meatball-n-jelly-doges_400w.png")$'>
</picture>
<figcaption>
The little one is Meatball, the big one is Jellybean. Both are very good boys.
</figcaption>
</figure>
$partial("partials/employment-opportunities.html")$
</div>
<p>
Every few months I try looping some electronic music, but I'm really bad at
it and I intend one day to share and self-deprecate.
</p>
<p>
When I'm away from the computer I'm spending time with my husband Dr. Corey
Blanchette and our two dogs, Jellybean and Meatball. Corey cooks and I do the
dishes if I can beat him to them. Most evenings we can be found on the patio
enjoying my latest mixology attempt while he tries to teach me ways to remember
different parts of human anatomy. For the most part all I can retain are the
acronyms that sound inappropriate, and I'm happy to share them over a drink.
One of my favorites is a mnemonic he was taught in school to name the "carpals",
which I'm pretty sure is a kind of bone.
</p>
<p>
Sometimes I make bad drinks, but fortunately we have a reliable backup. We call
it _The Bunny_, consisting of your choice of liquor, ginger beer, carrot juice,
fresh lime, and a little bit of orange liqueur over the top. Adjust to taste and
best served in a copper mug. Very refreshing, and it might even count as a serving
of vegetables!
</p>
</section>
<section>
<h2>My Life as It Relates To My Site</h2>
<p>
There is a large intersection between my personal and professional experiences.
Many engineers post about their lives in their technical blogs, sometimes sharing
deeply personal and difficult stories. I have the deepest of respect for their
bravery and great appreciation for the empathy their writing instills. Though there
are many events that have bled across my boundaries with work, I feel much
trepidation sharing how I feel and describing how I process the world around me. I
hope to share what is constructive, and that I can contribute to others' validation
of their life experience as others have for myself.
</p>
</section>

4
contact.md

@ -4,8 +4,6 @@ title: "Contact"
Please contact me via [blog@thisfieldwas.green](mailto:blog@thisfieldwas.green). I welcome conversations about [what I do]($route-to("about-me.html")$) or related subjects, and I'm happy to answer any questions and ask a few of my own!
## Regarding Jobs
$partial("partials/employment-opportunities.md")$
$partial("partials/employment-opportunities.html")$
More information about what I bring to the table may be found on my [about me]($route-to("about-me.html")$) page.

53
css/_default.sass

@ -1,5 +1,7 @@
@import "theme"
@import "code"
@import "components/code"
@import "components/figures"
@import "components/headers"
*
line-height: 1.6em
@ -14,6 +16,7 @@ html
font-size: $text-font-size
font-variant: discretionary-ligatures
text-align: $text-align-default
hyphens: auto
body
margin: 0
@ -24,8 +27,8 @@ body
> header
@include body-block
border-top: 0.7rem solid $border-color
border-bottom: $border-very-thick solid $border-color
border-top: $border-ginormous
border-bottom: $border-very-thick
> nav
font-size: 0
@ -36,7 +39,7 @@ body
& + a
margin-left: 0.5rem
padding-left: 0.5rem
border-left: $border-thin solid $border-color
border-left: $border-thin
nav
border-top: 0.1rem
@ -45,8 +48,8 @@ nav
footer
@include body-block
border-bottom: 0.7rem solid $border-color
border-top: $border-very-thick solid $border-color
border-bottom: $border-ginormous
border-top: $border-very-thick
padding-bottom: 3em
clear: both
> *
@ -62,39 +65,7 @@ a
color: dodgerblue
text-decoration: underline
h1, h2, h3, h4, h5, h6
line-height: 1.2em
color: $color-em-text
h1
font-size: 2rem
h2
font-size: 1.6rem
h3
font-size: 1.4rem
h4
font-size: 1.2rem
h5
font-size: 1rem
h6
font-size: 0.9rem
figure
@include bordered-figure
padding: 1rem 0.5rem
&[style*="float: right"]
border-left: $border-thin solid $border-color
figcaption
font-size: 0.9rem
.grid-container
*[role=grid-container]
display: grid
.post
@ -171,7 +142,3 @@ iframe
font-size: 0.8rem
@import "pages/about-me"
@mixin centered-headers
h1, h2, h3, h4, h5, h6
text-align: center

138
css/_media.sass

@ -1,138 +0,0 @@
///
/// Generates a media query based on a list of conditions
///
/// @param {Arglist} $conditions - Media query conditions
///
/// @example sass - With a single set breakpoint
/// @include media('>phone')
/// // ...
///
/// @example sass - With two set breakpoints
/// @include media('>phone', '<=tablet')
/// // ...
///
/// @example sass - With custom values
/// @include media('>=358px', '<850px')
/// // ...
///
/// @example sass - With set breakpoints with custom values
/// @include media('>desktop', '<=1350px')
/// // ...
///
/// @example sass - With a static expression
/// @include media('retina2x')
/// // ...
///
/// @example sass - Mixing everything
/// @include media('>=350px', '<tablet', 'retina3x')
/// // ...
///
@mixin media($conditions...)
@if ($im-media-support and length($conditions) == 0) or (not $im-media-support and im-intercepts-static-breakpoint($conditions...))
@content
@else if ($im-media-support and length($conditions) > 0)
@media #{unquote(parse-expression(nth($conditions, 1)))}
// Recursive call
@include media(slice($conditions, 2)...)
@content
///
/// Get operator of an expression
///
/// @param {String} $expression - Expression to extract operator from
///
/// @return {String} - Any of `>=`, `>`, `<=`, `<`, ``, ``
///
@function get-expression-operator($expression)
@each $operator in ('>=', '>', '<=', '<', '', '')
@if str-index($expression, $operator)
@return $operator
// It is not possible to include a mixin inside a function, so we have to
// rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
// functions cannot be called anywhere in Sass, we need to hack the call in
// a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
// Sass 3.3, change this line in `@if im-log(..) {}` instead.
$_: im-log('No operator found in `#{$expression}`.')
///
/// Get dimension of an expression, based on a found operator
///
/// @param {String} $expression - Expression to extract dimension from
/// @param {String} $operator - Operator from `$expression`
///
/// @return {String} - `width` or `height` (or potentially anything else)
///
@function get-expression-dimension($expression, $operator)
$operator-index: str-index($expression, $operator)
$parsed-dimension: str-slice($expression, 0, $operator-index - 1)
$dimension: 'width'
@if str-length($parsed-dimension) > 0
$dimension: $parsed-dimension
@return $dimension
///
/// Get dimension prefix based on an operator
///
/// @param {String} $operator - Operator
///
/// @return {String} - `min` or `max`
///
@function get-expression-prefix($operator)
@return if(index(('<', '<=', ''), $operator), 'max', 'min')
///
/// Get value of an expression, based on a found operator
///
/// @param {String} $expression - Expression to extract value from
/// @param {String} $operator - Operator from `$expression`
///
/// @return {Number} - A numeric value
///
@function get-expression-value($expression, $operator)
$operator-index: str-index($expression, $operator)
$value: str-slice($expression, $operator-index + str-length($operator))
@if map-has-key($breakpoints, $value)
$value: map-get($breakpoints, $value)
@else
$value: to-number($value)
$interval: map-get($unit-intervals, unit($value))
@if not $interval
// It is not possible to include a mixin inside a function, so we have to
// rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
// functions cannot be called anywhere in Sass, we need to hack the call in
// a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
// Sass 3.3, change this line in `@if im-log(..) {}` instead.
$_: im-log('Unknown unit `#{unit($value)}`.')
@if $operator == '>'
$value: $value + $interval
@else if $operator == '<'
$value: $value - $interval
@return $value
///
/// Parse an expression to return a valid media-query expression
///
/// @param {String} $expression - Expression to parse
///
/// @return {String} - Valid media query
///
@function parse-expression($expression)
// If it is part of $media-expressions, it has no operator
// then there is no need to go any further, just return the value
@if map-has-key($media-expressions, $expression)
@return map-get($media-expressions, $expression)
$operator: get-expression-operator($expression)
$dimension: get-expression-dimension($expression, $operator)
$prefix: get-expression-prefix($operator)
$value: get-expression-value($expression, $operator)
@return '(#{$prefix}-#{$dimension}: #{$value})'

25
css/_theme.sass

@ -1,5 +1,5 @@
@import "dist/include-media"
@import "fonts/code"
@import "media"
$text-font-family: "Georgia", serif
$text-font-size: 1rem
@ -16,24 +16,23 @@ $color-hover: lemonchiffon
$bg-content: white
$bg-greenfield: honeydew
$border-very-thick: 0.4rem
$border-thick: 0.3rem
$border-regular: 0.2rem
$border-thin: 0.1rem
$border-ginormous-width: 0.7rem
$border-very-thick-width: 0.4rem
$border-thick-width: 0.3rem
$border-regular-width: 0.2rem
$border-thin-width: 0.1rem
$border-color: change-color($color-greenfield, $green: 80%)
$border-color-sides: change-color($color-greenfield, $green: 80%)
$border-ginormous: $border-ginormous-width solid $border-color
$border-very-thick: $border-very-thick-width solid $border-color
$border-thick: $border-thick-width solid $border-color
$border-regular: $border-regular-width solid $border-color
$border-thin: $border-thin-width solid $border-color
@mixin body-block
margin: 0
padding: 1rem
@mixin bordered-figure
border-top: $border-thick solid $border-color
border-bottom: ($border-thick * 1.5) solid $border-color
// border-left: $border-thin solid $border-color-sides
// border-right: $border-thin solid $border-color-sides
box-sizing: border-box
@mixin scrollbars
&::-webkit-scrollbar
height: 0.5rem

5
css/_code.sass → css/components/_code.sass

@ -1,3 +1,5 @@
@import "figures"
code
color: $color-em-text
font-family: $code-font-family
@ -47,7 +49,6 @@ pre
code.sourceCode
@include bordered-figure
// background-color: $bg-greenfield
display: table
padding: 0.5rem 0
margin-bottom: 0.5rem
@ -64,7 +65,7 @@ pre
.line-number
display: table-cell
padding: 0 0.5rem 0 0.5rem
border-right: $border-thin solid $border-color-sides
border-right: $border-thin
text-align: right
user-select: none
text-decoration: none

24
css/components/_figures.sass

@ -0,0 +1,24 @@
@import "../theme"
@mixin bordered-figure
border-top: $border-thick
border-bottom: $border-very-thick
padding: 1rem 0.5rem
@include media('>=tablet', '<desktop')
&.figure-left
margin-right: 2rem
float: left
&.figure-right
margin-left: 2rem
float: right
figure
padding: 0
margin: 0
figcaption
font-size: 0.9rem
*[role=figure]
@include bordered-figure

27
css/components/_headers.sass

@ -0,0 +1,27 @@
@import "../theme"
h1, h2, h3, h4, h5, h6
line-height: 1.2em
color: $color-em-text
h1
font-size: 2rem
h2
font-size: 1.6rem
h3
font-size: 1.4rem
h4
font-size: 1.2rem
h5
font-size: 1rem
h6
font-size: 0.9rem
@include media('<=tablet')
h1, h2, h3, h4, h5, h6
text-align: center

136
css/media/_config.sass

@ -1,136 +0,0 @@
///
/// Creates a list of global breakpoints
///
/// @example sass - Creates a single breakpoint with the label `phone`
/// $breakpoints: ('phone': 320px)
///
$breakpoints: (
'phone': 320px,
'tablet': 768px,
'desktop': 1024px
) !default
///
/// Creates a list of static expressions or media types
///
/// @example sass - Creates a single media type (screen)
/// $media-expressions: ('screen': 'screen')
///
/// @example sass - Creates a static expression with logical disjunction (OR operator)
/// $media-expressions: (
/// 'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)'
/// )
///
$media-expressions: (
'screen': 'screen',
'print': 'print',
'handheld': 'handheld',
'landscape': '(orientation: landscape)',
'portrait': '(orientation: portrait)',
'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi), (min-resolution: 2dppx)',
'retina3x': '(-webkit-min-device-pixel-ratio: 3), (min-resolution: 350dpi), (min-resolution: 3dppx)'
) !default
///
/// Defines a number to be added or subtracted from each unit when declaring breakpoints with exclusive intervals
///
/// @example sass - Interval for pixels is defined as `1` by default
/// @include media('>128px')
/// // ...
///
/// /* Generates: */
/// @media (min-width: 129px)
/// // ...
///
/// @example sass - Interval for ems is defined as `0.01` by default
/// @include media('>20em')
/// // ...
///
/// /* Generates: */
/// @media (min-width: 20.01em)
/// // ...
///
/// @example sass - Interval for rems is defined as `0.1` by default, to be used with `font-size: 62.5%;`
/// @include media('>2.0rem')
/// // ...
///
/// /* Generates: */
/// @media (min-width: 2.1rem)
/// // ...
///
$unit-intervals: (
'px': 1,
'em': 0.01,
'rem': 0.1,
'': 0
) !default
///
/// Defines whether support for media queries is available, useful for creating separate stylesheets
/// for browsers that don't support media queries.
///
/// @example sass - Disables support for media queries
/// $im-media-support: false
/// @include media('>=tablet')
/// .foo
/// color: tomato
///
/// /* Generates: */
/// .foo
/// color: tomato
///
$im-media-support: true !default
///
/// Selects which breakpoint to emulate when support for media queries is disabled. Media queries that start at or
/// intercept the breakpoint will be displayed, any others will be ignored.
///
/// @example sass - This media query will show because it intercepts the static breakpoint
/// $im-media-support: false
/// $im-no-media-breakpoint: 'desktop'
/// @include media('>=tablet')
/// .foo
/// color: tomato;
///
/// /* Generates: */
/// .foo
/// color: tomato
///
/// @example sass - This media query will NOT show because it does not intercept the desktop breakpoint
/// $im-media-support: false
/// $im-no-media-breakpoint: 'tablet'
/// @include media('>=desktop')
/// .foo
/// color: tomato;
///
/// /* No output */
///
$im-no-media-breakpoint: 'desktop' !default
///
/// Selects which media expressions are allowed in an expression for it to be used when media queries
/// are not supported.
///
/// @example sass - This media query will show because it intercepts the static breakpoint and contains only accepted media expressions
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'desktop'
/// $im-no-media-expressions: ('screen')
/// @include media('>=tablet', 'screen')
/// .foo
/// color: tomato
///
/// /* Generates: */
/// .foo
/// color: tomato
///
/// @example sass - This media query will NOT show because it intercepts the static breakpoint but contains a media expression that is not accepted
/// $im-media-support: false
/// $im-no-media-breakpoint: 'desktop'
/// $im-no-media-expressions: ('screen')
/// @include media('>=tablet', 'retina2x')
/// .foo
/// color: tomato
///
/// /* No output */
///
$im-no-media-expressions: ('screen', 'portrait', 'landscape') !default

31
css/media/helpers/_logger.sass

@ -1,31 +0,0 @@
///
/// Log a message either with `@error` if supported
/// else with `@warn`, using `feature-exists('at-error')`
/// to detect support.
///
/// @param {String} $message - Message to log
///
@function im-log($message)
@if feature-exists('at-error')
@error $message
@else
@warn $message
$_: noop()
@return $message
///
/// Wrapper mixin for the log function so it can be used with a more friendly
/// API than `@if im-log('..') {}` or `$_: im-log('..')`. Basically, use the function
/// within functions because it is not possible to include a mixin in a function
/// and use the mixin everywhere else because it's much more elegant.
///
/// @param {String} $message - Message to log
///
@mixin log($message)
@if im-log($message)
///
/// Function with no `@return` called next to `@warn` in Sass 3.3
/// to trigger a compiling error and stop the process.
///
@function noop()

27
css/media/helpers/_no-media.sass

@ -1,27 +0,0 @@
///
/// Determines whether a list of conditions is intercepted by the static breakpoint.
///
/// @param {Arglist} $conditions - Media query conditions
///
/// @return {Boolean} - Returns true if the conditions are intercepted by the static breakpoint
///
@function im-intercepts-static-breakpoint($conditions...)
$no-media-breakpoint-value: map-get($breakpoints, $im-no-media-breakpoint)
@if not $no-media-breakpoint-value
@if im-log('`#{$im-no-media-breakpoint}` is not a valid breakpoint.')
@each $condition in $conditions
@if not map-has-key($media-expressions, $condition)
$operator: get-expression-operator($condition)
$prefix: get-expression-prefix($operator)
$value: get-expression-value($condition, $operator)
@if ($prefix == 'max' and $value <= $no-media-breakpoint-value) or
($prefix == 'min' and $value > $no-media-breakpoint-value)
@return false
@else if not index($im-no-media-expressions, $condition)
@return false
@return true

100
css/media/helpers/_parser.sass

@ -1,100 +0,0 @@
///
/// Get operator of an expression
///
/// @param {String} $expression - Expression to extract operator from
///
/// @return {String} - Any of `>=`, `>`, `<=`, `<`, ``, ``
///
@function get-expression-operator($expression)
@each $operator in ('>=', '>', '<=', '<', '', '')
@if str-index($expression, $operator)
@return $operator
// It is not possible to include a mixin inside a function, so we have to
// rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
// functions cannot be called anywhere in Sass, we need to hack the call in
// a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
// Sass 3.3, change this line in `@if im-log(..) {}` instead.
$_: im-log('No operator found in `#{$expression}`.')
///
/// Get dimension of an expression, based on a found operator
///
/// @param {String} $expression - Expression to extract dimension from
/// @param {String} $operator - Operator from `$expression`
///
/// @return {String} - `width` or `height` (or potentially anything else)
///
@function get-expression-dimension($expression, $operator)
$operator-index: str-index($expression, $operator)
$parsed-dimension: str-slice($expression, 0, $operator-index - 1)
$dimension: 'width'
@if str-length($parsed-dimension) > 0
$dimension: $parsed-dimension
@return $dimension
///
/// Get dimension prefix based on an operator
///
/// @param {String} $operator - Operator
///
/// @return {String} - `min` or `max`
///
@function get-expression-prefix($operator)
@return if(index(('<', '<=', ''), $operator), 'max', 'min')
///
/// Get value of an expression, based on a found operator
///
/// @param {String} $expression - Expression to extract value from
/// @param {String} $operator - Operator from `$expression`
///
/// @return {Number} - A numeric value
///
@function get-expression-value($expression, $operator)
$operator-index: str-index($expression, $operator)
$value: str-slice($expression, $operator-index + str-length($operator))
@if map-has-key($breakpoints, $value)
$value: map-get($breakpoints, $value)
@else
$value: to-number($value)
$interval: map-get($unit-intervals, unit($value))
@if not $interval
// It is not possible to include a mixin inside a function, so we have to
// rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
// functions cannot be called anywhere in Sass, we need to hack the call in
// a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
// Sass 3.3, change this line in `@if im-log(..) {}` instead.
$_: im-log('Unknown unit `#{unit($value)}`.')
@if $operator == '>'
$value: $value + $interval
@else if $operator == '<'
$value: $value - $interval
@return $value
///
/// Parse an expression to return a valid media-query expression
///
/// @param {String} $expression - Expression to parse
///
/// @return {String} - Valid media query
///
@function parse-expression($expression)
// If it is part of $media-expressions, it has no operator
// then there is no need to go any further, just return the value
@if map-has-key($media-expressions, $expression)
@return map-get($media-expressions, $expression)
$operator: get-expression-operator($expression)
$dimension: get-expression-dimension($expression, $operator)
$prefix: get-expression-prefix($operator)
$value: get-expression-value($expression, $operator)
@return '(#{$prefix}-#{$dimension}: #{$value})'

19
css/media/helpers/_slice.sass

@ -1,19 +0,0 @@
///
/// Slice `$list` between `$start` and `$end` indexes
///
/// @access private
///
/// @param {List} $list - List to slice
/// @param {Number} $start [1] - Start index
/// @param {Number} $end [length($list)] - End index
///
/// @return {List} Sliced list
///
@function slice($list, $start: 1, $end: length($list))
@if length($list) < 1 or $start > $end
@return ()
$result: ()
@for $i from $start through $end
$result: append($result, nth($list, $i))
@return $result

80
css/media/helpers/_to-number.sass

@ -1,80 +0,0 @@
///
/// Casts a string into a number
///
/// @param {String | Number} $value - Value to be parsed
///
/// @return {Number}
///
@function to-number($value)
@if type-of($value) == 'number'
@return $value
@else if type-of($value) != 'string'
$_: im-log('Value for `to-number` should be a number or a string.')
$first-character: str-slice($value, 1, 1)
$result: 0
$digits: 0
$minus: ($first-character == '-')
$numbers: (
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9
)
// Remove +/- sign if present at first character
@if ($first-character == '+' or $first-character == '-')
$value: str-slice($value, 2)
@for $i from 1 through str-length($value)
$character: str-slice($value, $i, $i)
@if not (index(map-keys($numbers), $character) or $character == '.')
@return to-length(if($minus, -$result, $result), str-slice($value, $i))
@if $character == '.'
$digits: 1
@else if $digits == 0
$result: $result * 10 + map-get($numbers, $character)
@else
$digits: $digits * 10
$result: $result + map-get($numbers, $character) / $digits
@return if($minus, -$result, $result)
///
/// Add `$unit` to `$value`
///
/// @param {Number} $value - Value to add unit to
/// @param {String} $unit - String representation of the unit
///
/// @return {Number} - `$value` expressed in `$unit`
///
@function to-length($value, $unit)
$units: (
'px': 1px,
'cm': 1cm,
'mm': 1mm,
'%': 1%,
'ch': 1ch,
'pc': 1pc,
'in': 1in,
'em': 1em,
'rem': 1rem,
'pt': 1pt,
'ex': 1ex,
'vw': 1vw,
'vh': 1vh,
'vmin': 1vmin,
'vmax': 1vmax
)
@if not index(map-keys($units), $unit)
$_: im-log('Invalid unit `#{$unit}`.')
@return $value * map-get($units, $unit)

41
css/media/plugins/_tweakpoints.sass

@ -1,41 +0,0 @@
///
/// This mixin aims at redefining the configuration just for the scope of
/// the call. It is helpful when having a component needing an extended
/// configuration such as custom breakpoints (referred to as tweakpoints)
/// for instance.
///
/// @param {Map} $tweakpoints [()] - Map of tweakpoints to be merged with `$breakpoints`
/// @param {Map} $tweak-media-expressions [()] - Map of tweaked media expressions to be merged with `$media-expression`
///
/// @example sass - Extend the global breakpoints with a tweakpoint
/// @include media-context(('custom': 678px))
/// .foo
/// @include media('>phone', '<=custom')
/// // ...
///
/// @example sass - Extend the global media expressions with a custom one
/// @include media-context($tweak-media-expressions: ('all': 'all'))
/// .foo
/// @include media('all', '>phone')
/// // ...
///
/// @example sass - Extend both configuration maps
/// @include media-context(('custom': 678px), ('all': 'all'))
/// .foo
/// @include media('all', '>phone', '<=custom')
/// // ...
///
@mixin media-context($tweakpoints: (), $tweak-media-expressions: ())
// Save global configuration
$global-breakpoints: $breakpoints
$global-media-expressions: $media-expressions
// Update global configuration
$breakpoints: map-merge($breakpoints, $tweakpoints) !global
$media-expressions: map-merge($media-expressions, $tweak-media-expressions) !global
@content
// Restore global configuration
$breakpoints: $global-breakpoints !global
$media-expressions: $global-media-expressions !global

43
css/pages/_about-me.sass

@ -1,23 +1,26 @@
.doge figure
width: 14rem
.intro
grid-template-columns: 1fr 1fr 1fr 1fr
grid-gap: 0 2rem
.meatball-headshot
grid-column: 4 / span 1
grid-row: 1 / span 2
.myself
grid-column: 1 / span 3
grid-row: 1
.life-itself
grid-column: 1 / span 3
grid-row: 2
.meatball-jellybean
grid-column: 1 / span 1
grid-row: 3
text-align: center
float: right
figcaption
text-align: left
text-align: justify
.tools
display: flex
flex-direction: row
flex-wrap: wrap
justify-content: flex-start
align-items: space-between
align-content: flex-start
section
width: 14rem
padding: 0 1rem 1rem
border-left: $border-regular solid $border-color
h3
margin: 0 0 1rem 0
ul
padding: 0 0 0 1rem
margin: 0
.life-on-site
grid-column: 2 / span 2
grid-row: 3

2
hie.yaml

@ -1,5 +1,5 @@
cradle:
stack:
cabal:
- path: "./site/main.hs"
component: "logans-blog:exe:site"

BIN
images/about-me/headshot-w-meatball-doge_768w.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

4
partials/employment-opportunities.html

@ -0,0 +1,4 @@
<h3>Looking for an Engineer?</h3>
<p>
For prospective employers, referrals, and recruiters: <em>I am happily employed</em>, though I welcome chats about prospects if you believe I am a good fit for your technical needs. I apply what works from my experience to the team context and reinforce what the team is already doing well.Sometimes my existing toolbelt doesn't work so well because unexpected problems can hard, and that's ok. Together we'll find a way to make things better.
</p>

19
src/Site/Compiler/CustomPandoc.hs

@ -1,9 +1,17 @@
module Site.Compiler.CustomPandoc where
import Hakyll
import Text.Pandoc.Highlighting (pygments)
import qualified Text.Pandoc.Options as Opt
customPandocExtensions :: Opt.Extensions
customPandocExtensions =
Opt.pandocExtensions <> customExtensions
where
customExtensions =
Opt.extensionsFromList
[ Opt.Ext_emoji
]
customPandocCompiler :: Compiler (Item String)
customPandocCompiler =
pandocCompilerWithTransformM readerOpts writerOpts return
@ -11,15 +19,16 @@ customPandocCompiler =
readerOpts :: Opt.ReaderOptions
readerOpts =
defaultHakyllReaderOptions
{ Opt.readerExtensions = defaultExtensions <> customExtensions
{ Opt.readerExtensions = defaultExtensions <> customPandocExtensions
}
where
defaultExtensions = Opt.readerExtensions defaultHakyllReaderOptions
customExtensions = Opt.extensionsFromList []
writerOpts :: Opt.WriterOptions
writerOpts =
defaultHakyllWriterOptions
{ Opt.writerHTMLMathMethod = Opt.MathJax ""
, Opt.writerHighlightStyle = Just pygments
{ Opt.writerHTMLMathMethod = Opt.MathJax "",
Opt.writerExtensions = defaultExtensions <> customPandocExtensions
}
where
defaultExtensions = Opt.writerExtensions defaultHakyllWriterOptions

9
src/Site/Rule/Sass.hs

@ -4,13 +4,14 @@ import Site.Common
sassRules :: Rules ()
sassRules = do
sassDependency <- makePatternDependency "css/**.sass"
sassDependency <- makePatternDependency "css/**.s(a|c)ss"
rulesExtraDependencies [sassDependency] $
match "css/main.sass" do
route $ setExtension "css"
compile sassCompiler
sassCompiler :: Compiler (Item String)
sassCompiler = getResourceString
>>= withItemBody (unixFilter "sass" ["--trace", "--stdin", "--load-path", "css"])
>>= return . fmap compressCss
sassCompiler =
getResourceString
>>= withItemBody (unixFilter "sass" ["--trace", "--stdin", "--load-path", "css"])
>>= return . fmap compressCss

Loading…
Cancel
Save