I wonder if we can create a JSS CSS compressor?

Life

Googele, react, etc use compressed CSS selectors, like AYxX

 

This would shrink our ultra verbose, human friendly CSS from hero-swerip__slide–let quite a bit. Maybe even 30%+; If we did the  modification in the <head> with pure JS, or somehow serverside (not possible in many envs), we should do it!

compress--my-long-nam

Our CSS is getting large even with BE. T

Raw JS (speedy) in ​<head>` would prevent FOUC.

Interesting idea. Ping me if already done or feedback.

 

One gotcha: <head> downloads all CSS… so that will still be slow. WIP!

 

Advertisements

iOS Safari iFrame and Z-Index Bug. Perceived Z index different from interaction z-index

Life

I just ran into an issue with iOS that had me scratching my head. We have a Vimeo integration in an iframe that was playing on click on iOS safari (as evidenced by sound and network) but none of our JS handlers were being triggered.

The same site on Chrome and others would work fine and our JS handlers would be triggered, specifically an image on top of the iframe bound to click.

The problem turned out to be a Z-Index issue but somehow, the apparent/perceived/visible z-index was different from the interaction/touch/collision detection z-index. Increasing the Z-Index on the ​image gave it the priority it needed to be clickable.

 

Jekyll – Custom Sort Order Next and Previous Posts with Loop

Life

To get the next and previous posts with a custom sort order with Jekyll, we need to do some fairly ridiculous logic with liquid.

Why is this worth posting? I’ve needed to do this multiple times, and this is the kind of thing you should be able to copy from the internet. Unfortunately each time I need it, “it doesn’t work” because of unintuitive liquid scope.

For example, you can’t increment the i variable in the below example inside of an if statement, but all’s well if you do it at the top level.

Anyway here’s the generic way to sort a Jekyll collection by an arbitrary sort key:

{% assign posts = site.products | sort: 'position' %}
{% assign i = 0 %}

{% for post in posts %}

  {% assign nextIndex = i | plus: 1 %}
  {% assign prevIndex = i | minus: 1 %}

  {% if post.title == page.title %}
    
  	we've looped through posts in our custom sort order 
  	until we've found our matching post.
    
    {% if forloop.first %}

      if it's the first loop, then the previous post is the last item
      {% assign prev = posts.last %}

    {% else %}

      otherwise, the previous post is the previous index
      {% assign prev = posts[prevIndex] %}

    {% endif %}


    {% if forloop.last %}

      if it's the last loop, then the next post is the first item
      {% assign next = posts.first %}

    {% else %}

      otherwise, the next post is the next index
      {% assign next = posts[nextIndex] %}
      
    {% endif %}

  {% endif %}

  {% assign i = i | plus: 1 %}
{% endfor %}

 

Shopify Checkout GA Cross Domain Tracking

Life

Grovemade.com has a very odd setup using Shopify. We have our own frontend layer using the Shopify API to populate products, with the rest of the site fully in our control.

This is done by having nginx proxy all requests to our app *except* shopify URLs such as /cart, /checkout

We then use the JS API and proxy requests to Shopify with a few fake headers to allow Shopify to work this way.

The biggest problem thus far has been e-commerce tracking, as Shopify does not let us control the checkout pages. E-commerce data arrives, but is not associated to any visitor session.

 

Shopify has a decorator that sets a _shopify_ga cookie immediately upon direct checkout links or cart submissions being executed. The response from Shopify appends parameters that identify the session on the previous domain to the checkout.shopify.com URL.

If you host pages outside Shopify and want cross domain support for Google Analytics, replicate the linker decorator and add the GA linker string in a cookie called _shopify_ga on click for checkout links or carts.

Any action that ultimately takes you to checkout.myshopify.com should add the _shopify_ga cookie with google linker parameter.

Here’s the relevant function to use on your site.

In our case, we had to modify the form element being looked for to match our environment.

 window.addShopifyLinkerLinks = function() {
  ga('require', 'linker');
  function addListener(element, type, callback) {
    if (element.addEventListener) {
      element.addEventListener(type, callback);
    }
    else if (element.attachEvent) {
      element.attachEvent('on' + type, callback);
    }
  }
  function decorate(event) {
    event = event || window.event;
    var target = event.target || event.srcElement;
    if (target && (target.getAttribute('action') || target.getAttribute('href'))) {
      ga(function (tracker) {
        var linkerParam = tracker.get('linkerParam');
        var cookie = '_shopify_ga=' + linkerParam + '; ' + 'path=/';
        document.cookie = cookie;
      });
    }
  }
  for (var i=0; i < document.forms.length; i++) {
    var action = document.forms[i].getAttribute('action');
    if(action && action.indexOf('/cart') >= 0) {
      addListener(document.forms[i], 'submit', decorate);
    }
  }
  for (var i=0; i < document.links.length; i++) {
    var href = document.links[i].getAttribute('href');
    if(href && href.indexOf('/checkout') >= 0) {
      addListener(document.links[i], 'click', decorate);
    }
  }
}

Hubspot API undocumented “Options” format

Life
HubSpot documentation ignores how to format the "Options" field in their API calls.

Their examples always leave it empty.

It returns an “Incorrect JSON format” error straight from Java if you try to pass an array of values.

The correct format is

[
{
u’description’:None,
u’value’:u’WALNUT &amp; LEATHER iPHONE CASE’,
u’label’:u’WALNUT &amp; LEATHER iPHONE CASE’,
u’readOnly’:None,
u’hidden’:False,
u’displayOrder’:-1,
u’doubleData’:None
}
]

I’m not sure what doubleData does.

Sublime Shortcut for Toggling Find/Search “In Selection”

Life

Sublime has shortcuts for almost all of the common search operations.

Find in file.
Find and replace in file.
Find and or replace in entire project.
Use regex
Match case
Match whole words

In selection is a very common, and useful tool.

You can add it by adding this to your user key bindings. I picked super+alt+s, which follows the rest of the search hotkeys. It overrides the "save all files" command, but only when the search window is open.

Plus, I hardly use that command, and when I need to, I use the command pallete.

    { "keys": ["super+alt+s"], "command": "toggle_in_selection", "context":
      [
         { "key": "setting.is_widget", "operator": "equal", "operand": true }
      ]
   }