Archiwum kategorii: CSS

itty.bitty

Post pobrano z: itty.bitty

Mark this down as one of the strangest things I’ve seen in a good long while. Nicholas Jitkoff has made a tool called itty.bitty that creates websites with all of the assets being contained within their own link. You can create a website without any HTML or CSS resources at all because it’s all been base64 encoded into the URL itself.

Take this crazy looking URL, for example:

https://itty.bitty.site/#How_it_works/XQAAAAJdDAAAAAAAAAAeHMqHyTY4PyKmqfkwr6ooCXSIMxPQ7ojYR153HqZD3W+keVdvwyoyd+luwncAksxo8PWJs+831jtAVty8rDpGXmyebtxMTP3PSa4g8/593sWue8WaPjJZKXPqJ+G6ffUrBFhdclCfPRZ1gVobusxdd5g4xI5328XhoOoeADUFQZMeRzfBVzcSlEdUqLcxhD5c7kYCBo79g1FR9+rRyw0U3Jyc+5Axt5HWUqKPcEBfQpO35dwvNhCOOZQ/83PRzl51VrpqJ+zwl7UT1JUUK24g/b9zCUO6EIGg1bcZW0VBL53SSK4LE+UkJNv3WKY04UQnISAISa4WghBZjXT5ID0S61leAohaQ9Kqkv6fGfZKI+RFa0bqnsYAHjZZQce8enN9zmfqYR/KIuNnoHmpY9M0W0okWY00CsBUYMdGh1tuE9dypij8nGhJnV61wgkafLJGNsg71mZFmumJRFRdjhuoORlUot67GJomgzUTr5dksBkTokx2zOQKMVW3M1yIQfy3ylY4txUf1icCDjf+Xs2Zhcm51cFucwhLfPIMfGfvlDOh4LtSQxlTu9YfZl4+H6fBMeVed/EqzEOwXAH21SiHUWBzxU8iwSNdHa48CvP41ozlThFVKKpWP290FN8UAcKd8smsf4YKLuDFHqoMyZILv1+k4ZhqEfXKO0Ky7oHaI7xLacv0mihhxK6Kz2jYlwFIsi3E8Mo8miWTIGcyZXCpwmsYzuKDigv5ZFGkw/VhOrQV/wLyqoa707i7PKnJE9BT1mhWpzonBLME6S7afa1CB3FqcPp7+/vRJbrKH9LqXpwUgzbdIl67YqgXBls5enwQ5mWgU23HC3Es60D0mBCALFNIsm/5HPLPWBwJrkfmx/JNPvnt2YLLeopCva1nY3cINXEfpGRtm2/j/F19PWFNJ0BW98k9KqaQNYduYlQN6Xjctewe2mRNM8k9CKaVet5pJ/eWshL8iMwje5sIX8MI09TGetVqAsgw6U+2F6UcqJPXFyJrGvdxeK6P5Zi9tSLQr5hByMK9mgXUfPuAL+Sazf/jt2UTMmHtr0yCaYTgw8bu/+Bzdo+eaE3J/ZVlTPPr2alSi6SmVSWGlYbcTIQZMA5KpF4GZFGowyxQUbndHhP/J1yY1SZ9G8YBktTaoGkXL2CDo6u498VrB6f7+oS5MOe3pK2/YzghZKLtMq1yzdnFlyekKyOSjppwbwkYJlLn96JOOpFxPHC2AZmUhfRbyYLNTrEQottY5z2Y/ERJTXMhTOtkE2Ba5LIrkrR3/7zjQTY5EF2plyx8xKwd07hrzZJ5tjUL3o2Hz+TsTs7glon64LbdmEZ6gH0acX7GrzdedQPdcElyDx+ByFW6l+IXDxShmeWlOBsMh0EOT5LC2y9UO9ZsNvdq21EAIiqTEOCf8xqm88r22q/A+uDmQHNhPG9s6zjoLmIaWugZWSy4Ab9wGC0+iTriRnLiePMQayw6bJ03G5Hoq+VKIF5y7OV2ijQtUkqqjDpGwkFSjwgirudMVeVhekZf7uMcCyT3JDLaTbMUVEtji2eL3fugR+yx8WYE99rczKeSpH7KBW4Vvs2cdy+42YZkZC9Ajnm3y1g6ljqE5/a3amHTXs4SH5TutdTeXKYI9fD6MHjmSWG2G8/JRjMGYVFhKpQs93CSaINQ3vGJYki5FP9aGDWqS7RlxXnUKgImK5upLy4XgmYQxQwoMYhj5JbfGyz5jqw+JlxK8XUkr+AQApWG/9suBe6BCgoAQRdgRjkMJGVdNwbCCCNs5ri/8kDwKahl6TAI4cO5B8QNg9KF1UalldlyMd3ITCHrcKkof1ZLGpHULGHvCcTC3IFANcd1hhNwyapdQPQQjiEqSBqP63kae39ehNXpbEjLk3vPypTX73p3D5/NxBUUqJgDSkKvs2zkKSfIXproiyqFQIxJgu+NFBYppkjkxQUzPRfhaojDES5jFdPMQGGCUG+en0zDihgNSJFnim/WHnUw89LXCQS96tbuW3Ym8RoGUIOzw9IjpWq8DJq3YY26TZu3WtqHUaSEUN0BBnUjAz+SR/hI3ellqVIK069hk8zGu7cdsRlGuRtUDnVeqMPX3M9VqGd7Qw8A9dV6XbjUP0DZTp4Xiqyj2IfXOG+qGAqCfPkKzaSW//XuArU=

This link contains all of the HTML and CSS that makes up the website. The really cool thing about this is that you don’t need a server to make an itty.bitty site — once you paste the link above into the browser it’ll fetch some code from the itty bitty domain then extract and inflate the data from there.

I’m not entirely sure how useful this is but it’s certainly a novel concept to me! Go ahead and start making your own itty.bitty sites.

Direct Link to ArticlePermalink

The post itty.bitty appeared first on CSS-Tricks.

Hyperlinking Beyond the Web

Post pobrano z: Hyperlinking Beyond the Web

Hyperlinks are the oldest and the most popular feature of the web. The word hypertext (which is the ht in http/s) means text having hyperlinks. The ability to link to other people’s hypertext made the web, a web — a set of connected pages. This fundamental feature has made the web a very powerful platform and it is obvious that the world of apps needs this feature. All modern platforms support a way for apps to register a URI (custom protocol) and also have universal links (handling web links in an app).

Let’s see why we’d want to take advantage of this feature and how to do it.

Why have app links at all?

Creating URIs that apps can open provides a unique set of benefits. A URL encapsulates the entire state of the webpage (it used to before the advent of Single Page Applications (SPAs) heavy with JavaScript and even in them, it is best practice to maintain this functionality) or a web application and it is advisable to have routing in applications so that a URL navigation is never broken. URLs have allowed browsers to have consistent back and forward buttons and reload and bookmark pages. And things like Google’s search depend on webpage addresses and web applications to navigate users to the correct place. Allowing applications to open URLs provides a way to link content and functionality in your application from other applications, websites and even internally within the application, like help/tutorial pages.

Trivia! Desktop applications have had hyperlinks even before the world wide web like Hypercard on the Mac in 1987 and Windows Help in 1990. The revolutionary thing about hyperlinks in the web is that you can link content that was created and is owned by others. This concept of cross app linking was not popular in traditional platforms until the rise of smartphones.

App links over the command line

There is already a shell for the command line interface and it supports passing arguments, redirecting the results from one application to anther, and even scripting. Still, the need for having support for hyperlinks is desirable for various reasons:

  • Security: The command line script is too complicated to get right. It is very risky to expect users of technology (who are not developers) to understand how to sanitize a command line script and make sure that the script does not accidentally call something, like rm -rf ~/Documents. The command line is not sand-boxed and though it provides a power, it puts responsibility on users and is prone to exploits.
  • Browser support: The browser cannot provide the command line interface. It is built to run untrusted third party code in a sandbox and that cannot be broken. And, if we don’t respect the rules of web technology, the app would silo itself out of the Internet. That is too much to lose.
  • Mobile: In the world of mobile, a touch keyboard is not as good and intuitive as the physical ones used on the desktop. Therefore, even if command line was present, it would be very difficult to use. All the power of redirection and chaining is not as effective as it is over a keyboard-based command line. A simpler solution like URL is more desirable in this context.
  • State: Theoretically, the command line interface can provide a way to have the application available in any state. But, practically, it was built for a separate purpose: to launch the application to perform a task and then return with a code. Additionally, it was de-prioritized by the GUIs in Windows and Mac. Many applications (like Microsoft Word and Adobe Photoshop) never had full-fledged command line support. They are not even present in the environment PATH and need the full path to be launched. URIs provide a fresh way to look at connecting information as well as functionality between applications. They are a secure, modern way of inter-app communication where the user does not have to think.

Linking Terminology

Deep Link

Deep link is the concept where a specific page/functionality within the website/application can be linked to. For example, https://css-tricks.com/link/to/this/page is a deep link for this page within the broader https://css-tricks.com link. Unlike superficial links that open applications, deep links open the application in a specific state, with passed data, ready to be consumed. Custom URI (described below) were the first ways to achieve deep linking within the app and „deep linking” in many contexts is now synonymous with custom URI, though it can also mean Universal links.

Custom URI

The web always had certain custom URIs that linked to default applications, like mailto:username@host.com and tel:1234567890 for email and telephone, respectively. It was an obvious choice to extend this and all major platforms provide a way for an app to register a URI protocol — like css-tricks://<link details> — that can open a deep linked native application. It should encapsulate the entire state and provide all routing benefits to open the app if it is available on the system. These provide a good interface for inter-app communication when the application is already installed and the desire is to open the user’s preferred application (like the browser or email client) that implements the desired protocol.

Custom URIs are great for the class of applications where the user wants to have a choice to select an app to perform a certain choice, but they are not great for the task of linking the website to the native application due to the following reasons:

  • Installation: Unlike the web, native apps need installation and, if they are not installed, you have two options: send the user to the app store (which also provides a custom URI) or properly fall back. Both these tasks require additional code which the URI does not encapsulate. The app store URI would be another separate URI that we need to store. Additionally, we have to detect whether the app is installed, which requires navigating to the URI and handling the error in navigation akin to a 404 since the protocol is not registered. This is not as simple as an anchor tag (<a href="{URL}"></a>) and, therefore, is a frequent source of developer complaints.
  • Lack of a central registry: Even if everything is done perfectly right, users can still be thrown into an application that they should not have been linked to. Unlike the DNS system (which guarantees uniqueness of domain names), application platforms do not have a central repository. So, multiple apps could register csstricks as a custom URI. Now, someone else could also register the same URI as one app and if the other application is installed on the system instead of the desired one, it could launch instead. The freedom of a custom URI for app selection works against the case where we always want to open a specific application.
  • Third Party Linking: Giving a custom URI to third parties because of the issues we’ve covered above is cumbersome and verifying them is painful. It is also a new protocol. Every website has links to the web content and updating the entirety of the Internet is not possible. It may not even be desired for SEO purposes.

Therefore, if we want to give the user the experience where the app is the preferred way to view content, custom URIs are not the perfect solution. This can be mitigated with smart app banners to some extent, where the banner would be displayed when the browser gets the custom URI and the app store link to identify the application. But this will not be seamless.

Trivia! URI and URLs are slightly different by definition, although they are used interchangeably. URI stands for Uniform Resource Identifier which means that it encapsulates everything required to get a resource. URL (Uniform Resource Locator) is a special type of URI that identifies the resource on the web. So, technically, a web address is a URL but something like csstricks:// would be just a URI.

These are all possible use cases where a custom URI might make sense:

  • You need to provide a URI that many third party client apps can register. Say you have a git:// in your app and the user’s favorite git client could register that URI and clone the repo when the the link is clicked.
  • You do not have a fully featured website.
  • You need a URI that is easy for users to remember.
  • You are willing to handle all edge cases related to navigation.

You can use custom URIs in parallel to the Universal link we’ll cover next and they can benefit from exposing the custom URL for inter-app communication while leaving the Universal link for the special case of web-to-app navigation. Android officially calls custom URIs deep links.

Control flows for Custom URI, Universal Link and Instant Apps
Universal link, App Link or App URI

The solution to the problem of web-to-app redirection can be solved with the Universal link (as it’s called in Mac and iOS), App Link (as it’s called in Android) or App URI (as it’s called in Windows UWP), which are different names of the same concept. This encapsulates the logic that every website needs to write in order to detect installed apps, launch correct pages and handle navigation failures in the case of custom URLs. It is very similar to the smart app banner in the sense that you need to provide an app store link to the application on your website for verification but it removes all the redundancy. Since the existing http(s) URL already contains the entire state, registering the custom URI is redundant. The original URL could itself navigate to the app with the website providing a fallback experience in case the application is not installed.

Once you register your app with a universal link, when the app is installed, the OS goes to the Internet to figure out the set of links that the app supports. Whenever any of those links get clicked, the native app gets launched instead of the browser. Full addressing support is now available in the application where a more customized experience can be provided falling back to the browser if the application is not installed. On important distinction with universal links is that they do affect regular browsing and therefore the OS providers keep then under tight lock and key.

A few good use cases for Universal links include:

  • You have a fully featured website for a fallback.
  • The desired flow for users is from the website to the app.
  • You have already built up a lot of karma by having content from your website linked around the web.

Instant Link or Deferred Deep Link

Deferred deep links provide the missing piece to the deep links if the user goes on to install the app when the link is opened. The user can be forwarded to the app store and the app store takes care of maintaining the context when the app is eventually launched after installation. This provides continuity in the case where an app is installed. Currently this method is only supported by Android (as Google Play Instant) and it is the option where you want to require users to move from the app to get the desired functionality. The hyperlink system on the web is seamless and clicking on a link gets you to the destination almost instantly (though a lot of things happen behind the scenes). Deferred deep links provide the same functionality for apps where clicking on a link can download the app and launch it with the correct page making all the tasks on app installation as seamless as possible.

You might consider using instant links if:

  • You need the users to use the app and not the website, even if they come from the browser (except for rare cases where they are low on disk space or have turned this feature off to save bandwidth).
  • Your key pages are always up-to-date in your application.
  • OK, always use them. With the extra SEO advantages that Google throws in for instant apps, there is no reason not to enable „instant” apps for an app that has Universal links.

Now that we have a summary of what each term means, let’s see how we can go about creating these on specific platforms.

Setting up application hyperlinks

MacOS and iOS

Custom URI

Registering a custom URI in MacOS and iOS is extremely easy. In Xcode, go to the Info.plist file of the project and select the option URL Types. Create an item of type URL Schemes inside the file where you can add all of the URL schemes you wish to support.

Note: The default URL Identifier filled in by Xcode within URL Types is optional.

URL Scheme in Xcode
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>css-tricks</string>
    </array>
  </dict>
</array>

For MacOS, you will receive calls to the AppDelegate where you can override the function:

func application(_ application: NSApplication, open urls: [URL]) {
  // Select the first URL in the list of URL to open
  let url = urls[0]; 
  // Log the entire URL
  NSLog("%@", url.absoluteString)
}
-(void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls {
  NSLog(@"%@", urls[0].absoluteString);
}

In iOS, the function to receive the same call in AppDelegate is:

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
  NSLog("%@", url.absoluteString)
  return true
}
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options: (NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  NSLog(@"%@", url.absoluteString);
  return true;
}

The URL type in all these cases contains the usual URL pieces like the path, the query and the search parameters.

Universal Links

Universal links are not supported in MacOS. To get universal links on iOS, you need to be registered with the Apple Developer Program or be an Apple Developer Enterprise Program member. Universal links on iOS are part of a wider concept of associated domains where the secrets of the website, like stored credentials, can be accessed from within the app and Apple does not allow regular Xcode users to dabble with universal links (if you accidentally enable the functionality for your App ID but do not put this in the entitlements, be prepared to get a validation error: ERROR ITMS-90046: „Invalid Code Signing Entitlements”).

Trivia! Universal links are a new concept and many of the Mac and Windows desktop applications are built with their own network stack, do not use the app store and may have been released years ago. Therefore, the OS does not have the control required to force all applications to follow the Universal link concept and open the app. Apple has chosen to not implement Universal Links in MacOS yet, while on Windows they only work in the Microsoft Edge browser and other UWP apps (which use the same technology).

To register for Universal links on iOS, you need to do the following things:

  1. Enable the App ID on the Apple Developer website to get the feature for associated domains.
  2. Add the associated domains to the entitlements file.
  3. Add a apple-app-site-association file to the web domain to provide ownership verification for the application.
  4. Add code to handle clicks on Universal links.

Steps 1 and 2 can be performed together in the Capabilities tab on Xcode if you are signed in and have the right profile selected (which is from an Apple Developer or Enterprise account). The Xcode UI provides an indication that the both items have been completed.

Associated Domains in the Capabilities section in Xcode

In the image above, Step 2 has been successfully completed, while Step 1 is pending. You can click on the exclamation mark to find the issue. It is important to prefix the domain with applinks: (that identifies that you need app links functionality; the same is used for shared credentials). This identifies that the App Link capability has been enabled.

The above steps can also be accomplished individually.

For Step 1, enable Associated Domains in the App ID section on the developer website (shown at the bottom of the image):

Apple Developer website to enable associated domains

For Step 2, add the following to the entitlements file:

Entitlements plist
<key>com.apple.developer.associated-domains</key>
<array>
  <string>applinks:css-tricks.com</string>
</array>

Now, for Step 3, host a association file apple-app-site-association like https://css-tricks.com/apple-app-site-association with mime type application/json with the following JSON data from the root of your website:

{
  "applinks": {
    "apps": [],
    "details": {
      "ABCDEFGHIJ.com.ccs-tricks.mobile": {
        "paths": [
          "*"
        ]
      }
    }
  }
}

The above snippet grants the app with ID ABCDEFGHIJ.com.ccs-tricks.mobile and provides access to all requests on the https://css-tricks.com domain. Note that this works with https but http is not supported. You can also use an alternative location like https://css-tricks.com/.well-known/apple-app-site-association for this. This URL is preferred as it can keep the Android, iOS and UWP association files in a safe separate folder that cannot be accidentally deleted.

Trivia! Universal links do not guarantee that the app is opened. If the user goes back to the website from the header in iOS, the OS decides to default to the website from then on until the user decides to use the app by clicking on the header again from Safari.

For Step 4, add the following code to the app:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
  if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
    let url = userActivity.webpageURL!
    print(url.absoluteString)
  }
  return true
}
-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
  if(userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
    NSLog(@"%@", userActivity.webpageURL.absoluteString);
  }
  return true;
}

Smart app banners on iOS are much simpler and can be added via a simple meta HTML tag in the head section like this:

<meta name="apple-itunes-app" content="app-id=123456789, app-argument=https://css-tricks.com/linkto/this/article, affiliate-data=optionalAffiliateData">

They call the same method as Custom URIs we covered earlier.

Windows (Traditional)

On the traditional Windows platform (Win32/.NET), the custom URI (called Custom Pluggable Protocol Handler) is the only feature supported. Adding a custom URI in Windows involves adding a registry entry within HKEY_CLASSES_ROOT. The following will open the app with the absolute path given when clicking on a link to css-tricks://<text>. Due to using the command line, this always opens a new instance of the app and does not send the URL to the existing running application.

Registry Entry for providing application name
Registry entry for open command
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\css-tricks]
"URL Protocol"=""
@="CSS Tricks (Application name)"

[HKEY_CLASSES_ROOT\css-tricks\shell]

[HKEY_CLASSES_ROOT\css-tricks\shell\open]

[HKEY_CLASSES_ROOT\css-tricks\shell\open\command]
@=""C:\\Windows\\notepad.exe" "%1""

The above launches notepad with the command line argument corresponding to the URL supplied. Be aware that Notepad does not support custom URI and will not know what to do with the URL.

This behave is similar to passing this on the console and the application needs to make sure it properly distinguishes between the argument being a regular CLI or a custom URI, as shown here:

namespace ConsoleApplication1 {
  class Program {
    static void Main(string[] args) {
      if (args.Length > 0 && args[0].IndexOf("css-tricks:") == 0) {
        Console.Write(args[0]);
      }
    }
  }
}
int main(int argc, char*argv[]) {
  if (argc > 1) { // argv[0] is the file name.
    std::string word(argv[1]);
    if (word.compare(0, 11, "css-tricks:") == 0) {
      std::cout<<word;
    }
  }
  return 0;
}

Universal Windows Platform (UWP)

On the Universal Windows Platform, you can use the package manifest to register both the custom URI and the Universal link (called App URI Handler).

Custom URI

Add a protocol declaration in the package.appxmanifest:

Protocol Declaration in package.appxmanifest

The same can be achieved in code:

<Extensions>
  <uap:Extension Category="windows.protocol">
    <uap:Protocol Name="css-tricks" DesiredView="default">
      <uap:DisplayName>CSS Tricks</uap:DisplayName>
    </uap:Protocol>
  </uap:Extension>
</Extensions>

…and this can now be handled within the app.

protected override void OnActivated(IActivatedEventArgs args) {
  base.OnActivated(args);
  if (args.Kind == ActivationKind.Protocol) {
    var e = args as ProtocolActivatedEventArgs;
    System.Diagnostics.Debug.WriteLine(e.Uri.AbsoluteUri);
  }
}
// In App.xaml.h
virtual void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs^ e) override;

// In App.xaml.cpp
void App::OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs ^ e) {
  if (e->Kind == Windows::ApplicationModel::Activation::ActivationKind::Protocol) {
    auto args = (ProtocolActivatedEventArgs^)e;
    auto url = args->Uri->AbsoluteUri;
  }
}
Universal Links (App URI)

App URIs are only supported in the Microsoft Edge browser. They do not work in Internet Explorer, Chrome or Firefox. App URIs also have a package entry similar to the custom URI. It is not available in the UI of Visual Studio Code 2017. The package.appxmanifest entries are almost the same:

<Extensions>
  <uap3:Extension Category="windows.appUriHandler">
    <uap3:AppUriHandler>
      <uap3:Host Name="css-tricks.com" />
    </uap3:AppUriHandler>
  </uap3:Extension>
</Extensions>

If uap3 is not available, it can be added to the Package tag where uap is also defined:

xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"

App URI is a relatively new feature of Windows and many UWP projects target older versions of windows as the minimum version. You might need to bump that up in order to support this feature.

Just like iOS, the website needs to verify the ownership of the domain for this to function. The can be done by hosting a file with mime type application/json at the root of your website, like https://css-tricks.com/windows-app-web-link or https://css-tricks.com/.well-known/windows-app-web-link over https with the content:

{
  "packageFamilyName": "YourPackageFamilyNameHere",
  "paths": ["*"],
  "excludePaths": ["/none/*", "https://cdn.css-tricks.com/robot.txt"]
}

To get the package family name, run the following in Powershell and search for your package path:

Get-AppxPackage

Handling App URIs requires the same code as custom URIs on Windows. By design, all you need to do is see the protocol field in the provided URI and write the corresponding logic.

Just like iOS, Windows users have a choice to disable opening apps. Windows provides registry settings to force apps to open (used for testing) and also a validator tool (located at %windir%\system32\AppHostRegistrationVerifier.exe) to verify if the above settings are correct.

Android

Custom URI

Android has supported custom URIs from the very beginning. This can be done with code. In AndroidManifest.xml add:

<activity android:name=".CustomUriActivity">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="css-tricks" />
  </intent-filter>
</activity>

The category DEFAULT is to ensure that there is no user action needed to enable it. BROWSABLE ensures that the custom URIs work in the browser.

This can then be handled in the CustomUriActivity.java on creation:

public class CustomUriActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Intent intent = getIntent();
    if (Intent.ACTION_VIEW.equals(intent.getAction())) {
      Uri uri = intent.getData();
    }
  }
} 
Universal Links

Universal links in Android are very similar to App URIs. Android Studio provides a GUI tool to create this. Go to Tools > App Link Assistant. This will provide the three tasks needed to create app links:

App Link Assistant in Android

For Step 1, enter the URL mapping editor and click on the + button to open the URL mapping dialog:

URL Mapping dialog

Once you click OK, it will show the code that could have been used alternatively in the AndroidManifest.xml:

<activity android:name=".CustomUriActivity">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="http" android:host="css-tricks.com"/>
  </intent-filter>
</activity>

Note: This exactly the same as as it is for a custom URI. In the Step 2, select the activity you assigned and it will add the code to load the action. The code is also exactly the same as was used for custom URIs above.

In the Step 3, it will generate the JSON file that needs to be added to the website as application/json mime at https://css-tricks.com/.well-known/assetlinks.json.

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "css_tricks",
    "package_name": "com.css-tricks.android",
    "sha256_cert_fingerprints":
    ["17:CC:87:9C:C8:39:B1:89:48:E8:2E:9E:6F:FC:7D:03:69:4D:05:90:2A:84:B8:AE:5D:6B:30:97:F8:1C:2B:BF"]
  }
}]

Android Studio automates the fingerprint generation that verifies the application identity. This identity is different for debug and the release version of the application.

Instant Link

Instant apps on Android require minor changes from the App links we covered earlier. To enable instant apps, modify the intent declared above to look like this:

<activity android:name=".CustomUriActivity">
  <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="http" android:host="css-tricks.com"/>
    <data android:scheme="https"/>
  </intent-filter>
</activity>

This allows both http and https URLs to be associated with the app, and the autoVerify tells the Android (Google Play) store to verify the manifest item automatically. Instant app is a Play Store/Google Chrome feature. The changes on the app are minimal to support this.

Electron

Electron apps are traditional MacOS and Windows apps with the chromium runtime. These platforms only support custom URIs that can be enabled without touching the compiled code.

For MacOS, edit info.plist as described in the MacOS section. The events are received by the app object as:

// In main.js
require('electron').app.on('open-url', (event, url) => {
  console.log(url);
});

For Windows, add the registry entry as defined in the Windows section. The app will receive the custom URL in process.argv:

// In main.js
console.log(process.argv[2])

In electron, you can load external web pages that can open URLs. If it is desired to handle only those URLs internally, that can be registered via an electron API:

require('electron').protocol.registerHttpProtocol('css-tricks', (req, cb) => {
  console.log(req.url)
});

Web

Going a complete circle, a website can also register custom URIs (like handling mailto: links via a website). Note that these only affect links provided in other websites and have nothing to do with links in other applications. The API is very restrictive and can be used only for a selected link type: web+<insert anything here>. Browsers allow a pre-defined list of top level protocols to be registered:

  • Chrome: mailto, mms, nntp, rtsp, webcal
  • Firefox: bitcoin, geo, im, irc, iris, magnet, mailto, mms, news, sip, sms, smite, ssh, tel, urn, webcal, wti, xmpp

To register a protocol, you need to provide the same domain as the website that registers it (i.e. https://www.css-tricks.com can register only https://www.css-tricks.com and not https://css-tricks.com).

if (navigator.registerProtocolHandler) {
  navigator.registerProtocolHandler("web+csstricks", "https://css-ticks.com/?s=%s", "CSS Tricks custom URI");
}

The custom URIs are not indexed by search engines and therefore there is not much use to these apart from handling the links like mailto, webcal etc., that are already present in the web at multiple places.

Wrapping Up

Adding hyperlinks to native apps is an easy and effective way to seamlessly move the user across applications with graceful handling in case the application is not installed. With these links, applications can have provide paths all over the Internet which can be an effective promotional mechanism. Links also give websites a way to provide data and launch the applications on a specific page without the security worries that plague the downloadable files or command line scripts allowing effective tutorials and bug reports. Routing and URLs provide for the core features of the World Wide Web like navigation, the back button, bookmarks and search. Hyperlinks in apps bring these learning from the (almost) 30 years of world wide web to native applications.

The post Hyperlinking Beyond the Web appeared first on CSS-Tricks.

Random Day in the Life of a Developer

Post pobrano z: Random Day in the Life of a Developer

Yesterday, I started going through my email as soon as I got to work. I always start my day with email. I kinda like email. I read some interesting things in keeping up with the industry. I deal with some business things. I handle with some personal things. I make a note of the most important stuff going on.

Then I kept working!


I fixed a bug that was affecting Firefox exclusively. This code would set some elements in a row with flexbox, let them wrap, then looked at all of them to see which ones wrapped and which ones didn’t. Then it put the ones that wrapped in an overflow menu. Seemed simple enough. But some of the items were in bold text and some were not. The ones that were not bold were somehow pushed down 1.5px in Firefox (but in no other browser I looked at). The code checked to see if the top position was anything higher than 0 in order to check if they wrapped, and thus incorrectly marked some elements as being wrapped. The code had to be loosened up to accommodate that.


I tweeted some things that looked particularly interesting that day.


I had logged a bug a few days ago where a couple of small buttons were not styled or aligned how they should be in a common modal dialog. In diagnosing the issue, it looks like we had a weird git merge where some newer CSS was merged in that was assuming some HTML that wasn’t quite live yet.

I decided the cleanest way forward was to make the small HTML change forthcoming in the new branch in a new fixer branch that could go out quickly. The alternative was reverting the CSS back and my guess was that would have caused worse merge conflicts later, plus it was worth taking advantage of the newer, nicer styling now.

Then, because of course it did, the small HTML change caused another bug. The JavaScript that was watching those buttons couldn’t find them anymore and I failed to test it or, ideally, write a test for it. So, I had to fix that too, but sadly only after a user reported it. Gosh, I gotta improve our integration tests there.


I was in two meetings. One was a standard weekly catchup meeting. One was with a new person I’d never met before and we were exploring if there was an interesting way we could work together.


I fixed a bug with some bad backend logic (that I had written myself, of course). I wanted to display some information to a user based on, I thought, if they were on a team or not. I got that logic right, but it turns out the messaging was depended on that team being active (i.e. not expired in some way), so I had to update the logic to account for that. My team member helped me think it through because I didn’t understand the way billing and active subscriptions were tied to the user well enough.


I fixed a bug where a UI element was showing a strong warning state, but there was no way to find out what the problem was. It was a big red box with an exclamation point in it. It even had a hover state. But clicking it did nothing.

Turns out it was just a problem with event delegation. It was being added to the page after click handlers attempted to bind two elements like that. I fixed it by re-binding those click handlers after of the error UI was appended. It didn’t feel great. A better fix might have been updating the event delegation to be more efficient about this case. But, I’d been burned by that in the past, so this fix seemed more robust, even if less elegant and efficient.

It made me pine for the page being ported to React, where a silly event delegation bug would have never been an issue. Not long after, I fixed another bug where a click handler had been bound to a span.class_name, and the span was updated to be a button. That was a semantic and accessibility win, but broke the handler.

Again, something that just doesn’t happen in React.


Then I wrote this blog post and went home.

The post Random Day in the Life of a Developer appeared first on CSS-Tricks.

Delivering WordPress in 7KB

Post pobrano z: Delivering WordPress in 7KB

Over the past six months, I’ve become increasingly interested in the topic of web sustainability. The carbon footprint of the Internet was not something I used to give much thought to, which is surprising considering my interest in environmental issues and the fact that my profession is web-based.

The web in a warming world

As a brief recap, I attended MozFest in London last year. In between sessions, I was scanning a noticeboard to see what was coming up, and I spotted a session entitled, „Building a Planet-Friendly Web.” I felt a little dumbstruck. What on Earth was this going to be about?

I attended the session and the scales fell from my eyes. In what now seems obvious — but at the time was a revelation — I learned of the colossal energy demand of the Internet. This demand makes it the largest coal-fired machine on Earth, meaning that its CO₂ emissions are probably at least equivalent to global air travel. More and more people are coming online, but this, coupled with the rise of ever more obese websites, means that the Internet’s energy demands are growing exponentially. Every additional byte transferred means more energy, which in most countries means more CO₂.

It is widely accepted that we should avoid printing emails to save trees, but this leads to a false assumption that the web is a green medium. I’m afraid to say, it is not.

Is there another way?

The good news is, it needn’t be this way. Since 2009, Greenpeace has been auditing Internet companies on their energy usage, and lobbying them to improve by way of their Clicking Clean campaign. The Green Web Foundation has an ever-growing database of hosting companies who use renewable energy, or who are at least committed to being carbon neutral. And new tools are emerging to help those working on the web measure the environmental impact of our products.

There are also some great resources out there such as Tim Frick’s Designing for Sustainability, three year’s worth of talks from the SustainableUX conference, and peripheral material that, while not explicitly focused on sustainability, can help us build lower carbon websites.

Enter Susty WP

I decided to challenge myself to see how efficiently I could package a WordPress website. While I was at it, I thought, “Why not make the website about sustainability and WordPress?” I know, WordPress/sustainability inception. Cool, huh?

And so, sustywp.com is born. With its accompanying theme, Susty. I’m rather chuffed to say I managed to get the load of the homepage down to 7KB of data transfer. I did actually get it down to 6KB, but the meta stuff added by Yoast bumped it up almost a whole KB! 😛 However, given I’m trying to spread a message, I deemed this to be an acceptable trade-off.

Before I get into the nuts and bolts, I just want to share a few headline facts:

So, how does it work?

The remarkable thing about this is actually how mundane it is. Heavily inspired by Heydon Pickering’s evangelizing about writing less damn code, and projects like Brad Frost’s Death to Bullshit, I embarked on a root and branch pruning of Underscores, removing everything I possibly could. And to be clear, it’s not as though Underscores is full of cruft. It’s a very lean starting point for building a theme. I’m a great admirer of it and thoroughly endorse using it. However, it includes certain assumptions that didn’t chime with my spartan goal of making the smallest possible WordPress site.

Such as, you ask?

Nav? No. Sidebar? Get rid of it. Classes? Not in my socialist utopia.

Given the constraints I had set, anything making it to the DOM had to have a very good reason to be there. With this being an experiment, I took the somewhat radical approach of relegating the navigation to its own page. This is wonderfully liberating and means I didn’t have to go through the almost always disappointing dance of wondering how I would make it look presentable, hide it for the many users who won’t care, make sure it’s robust enough to be infinitely long with never-ending dropdowns, etc.

This means less HTML, less CSS, and definitely no JavaScript! Win, win, win.

As for the sidebar? Well I’ve always had an aversion to sidebars, so that was an easy thing to remove. Susty has no widget area. I’m sure it’ll get over it.

Is there more fat we can trim? Well, if we’re going to have hardly any HTML, and we want minimal CSS, do we really need all those classes? I wanted to try my hand at writing CSS in the way it was intended to be used, instead of bowing to the class-ititis and div-ititis that pervade the industry.

You might not need web fonts

Alongside images, video and JavaScript, the other item that has become a significant factor in bloated websites is web fonts. Given that it’s now a Silicon Valley fashion for every startup to commission its own typeface as a first order of business, it seems as though every website you visit incurs downloading a bunch of font files. It may not seem so bad to tack on a hundred kilobytes for some nice type, but when you multiply this by the many different people visiting your website across different devices and browsers, fonts can easily account for hundreds of megabytes of data transfer — if not gigabytes/terabytes/petabytes for higher traffic websites.
Custom web fonts are also a potential antipattern for our users, for whom:

  1. Page loads are slowed down as they see either a flash of unstyled text or, potentially even worse, no text while the font loads.
  2. By the time the font has loaded, they don’t notice or care.

If you do have to use custom fonts or are in combat with the flash of un-styled text, font-display is an new property designed to instruct the browser how to handle font downloads. This post goes into it even more.

Almost every device these days has a half-decent set of fonts installed, so why not compose a stack of potential fonts you’d like to see, ending with either “serif “ or “sans-serif.” Or embrace those default fonts altogether with a system font stack.

I wanted Susty to have a bit of a Swiss Style aesthetic, so my font stack ranges from the perfectly proportioned Helvetica (which all Apple devices have by default) to sans-serif, by way of Arial, Frutiger and Nimbus Sans L.

As an Ubuntu user, I see Arial, which is perfectly pleasant. On my Android devices I see the default sans-serif font (Droid Sans I believe), which actually works rather well on mobile — you’d almost think that’s what it was designed for!

What else? Standard stuff. Few or no images and SVG where possible

Beyond this, it’s really just the standard. The homepage of sustywp.com has no images and just one SVG which is loaded inline rather than by an HTTP request. The CSS is minified. Everything gets Gzipped. The server is tuned with NGINX and Fast-CGI cache to serve content from memory.

Bonus SPA-like features without the work or code

When I was boring my girlfriend, Daisy, with my plans, I asked her how she felt conceptually about a website not having a navigation, and it instead being on its own page, found via a „menu” button. She thought it sounded ok. So, when I came to show it to her, I watched eagerly as she clicked the menu link. Her response was that she didn’t really think about clicking to see the menu, and the menu appearing, as changing pages. This confirmed one of my fears that my previous obsession with eliminating page loads when building Single Page Apps (SPAs) is something that only really bothers web developer-y types. Most people don’t really notice it or think about it. So, if you make your website lightning fast, the page loads themselves are almost imperceptible anyway.

With that being the case, I figured why not just pretend it was a menu overlay? So, I added a cross dismiss button and a tiny bit of JavaScript to take you from the menu back to the previous page if you click it. (I did initially implement this in PHP using the $_SERVER superglobal and HTTP_REFERER, but this breaks when using something like FastCGI cache to serve pages (i.e. those pages there is no $_SERVER superglobal.)

There’s still more to come

We all know that things can always be better. Here are a few of the things I’m working on to improve Susty:

  • Finish reading Jeremy Keith’s book Going Offline so I can implement a Service Worker and make Susty even more performant.
  • Look to see how I can implement Gutenberg. It’s going to add a bunch of kilobytes and that makes me sad! 😉
  • Currently, sustywp.com is hosted in one data center in London, and served globally from that location. This means that someone in Wellington, New Zealand is not going to get the full experience! I have a longer-term plan of establishing my own little CDN of edge servers (each one hosted by a 100% renewable energy powered host in each location that I can find one) just running NGINX and serving cached versions of the site. This is a simplified way of how we do things on WordPress.com VIP.

In conclusion

Susty is an example of taking sustainable design techniques to an extreme. I’m not suggesting that we should all start building websites with this exact method. But I do think sustainability should be a much higher priority in the web design industry. It’s good to think about saving any kilobytes we can, and to think in terms of kilobytes rather than megabytes. If you do want to use a JavaScript interface library, why not try out Choo.js, which has React-like features and comes in at just 4KB. Remember, NASA managed to put a man on the moon with about 50KB of data storage at their disposal.

The best thing about all this is that efficient websites means faster websites, which means:

  • Happier users
  • Better search engine placement
  • Happier servers

The post Delivering WordPress in 7KB appeared first on CSS-Tricks.

Writing Good Support Requests

Post pobrano z: Writing Good Support Requests

My take on trying to be helpful to a support staff.

One bit is just as relevant for learning development:

Writing out a ticket will help you figure out the problem.

Sometimes when you have to take a second to collect your thoughts and explain something, the problem will become clear and maybe even the solution. Oftentimes, a bug is a bug and just needs to be fixed — but sometimes your support ticket might actually be something you can sort out for yourself and writing things out might be the first step toward that.

You know what they say, the best way to learn something is to teach it.

Just replace „ticket” with „forum topic” or whatever, on something like Spectrum.

Direct Link to ArticlePermalink

The post Writing Good Support Requests appeared first on CSS-Tricks.

One-Offs

Post pobrano z: One-Offs

There is this sentiment that you don’t design the homepage of a site first. For most sites, it’s an anomaly. It’s unlike any other page and not something to base the patterns you use for the rest of the site or help inform other pages.

You might call it a one-off.1

One-offs are OK! A world without one-offs is very boring. But a site chock-full of one-offs leads to familiar problems: inconsistency and non-reusable CSS that leads to bloating and maintainers that don’t really know what’s used and what isn’t.

What are we to do?

Brad Frost thought about this recently with his article Where to put one-off components?:

It’s quite likely that individual applications contain components that fit the very real needs of a specific application (think calculators, holiday-season parallax hero units, context-specific interactive maps, and so on), but may not be able to be immediately (or ever) abstracted into a generic, reusable component that’s included in the design system.

And Ethan Marcotte:

Here’s an example: let’s say you’ve designed a splashy-looking hero image. (Nice work, by the way. It looks very splashy.) Let’s also say its scope is fairly limited: perhaps it’s being used on a small number of pages, or it’s tied to one specific section of your website. In other words, it’s generic enough to be a pattern, but it’s not a widely-used one.

As you might have guessed, this is a dangerous place for a pattern to be. If a pattern feels a little idiosyncratic, that should always prompt us to ask: should your team keep the pattern, or remove it?

Thing is, there’s no single, easy answer to that question. Every pattern is different, and each pattern’s value is variable. Maybe we’ll drop that pattern altogether; maybe we’ll combine it with another, similar pattern

Brad connected the idea of a one-off to Harry Robert’s shame.css concept:

The idea of shame.css is that you have a totally new stylesheet reserved just for your hacky code. The code you have to write to get the release out on time, but the code that makes you ashamed.

But maybe a CSS file like that doesn’t need to be reserved just for „hacks” or shamefully quickly-written fixes (hey, it’s better than inline styles), but also for one-offs.

From a developer-in-charge-of-styling perspective, it’s interesting to consider how different styling methologies come into play here. For those travelling the atomic CSS road, in a sense, everything is a one-off. You might still have a pattern that is visually or behaviorly a one-off because of how you’ve built it, but not because it uses a different set of CSS. Callum Jefferies on his experience with atomic CSS:

I no longer had to think about how to organise my CSS. I didn’t have to think about what to name my ‚components’, where to draw the line between one component and another, what should live where, and crucially, how to refactor things when new requirements came in.

So, too, with the 36 flavors of CSS-in-JS. When your styles are attached to your components, all components are one-offs. Stop using that component, stop using those styles.


1 Apparently one-off is a fairly new expression:

As William Safire observed in a 2007 On Language column, one-off meaning „something unique” is a British expression that has been creeping into American speech and writing in recent years. And as with other Briticisms that impinge on these shores (gone missing comes to mind), the idiomatic origins of one-off are mostly lost on American ears.

The post One-Offs appeared first on CSS-Tricks.

CSS Grid in IE: CSS Grid and the New Autoprefixer

Post pobrano z: CSS Grid in IE: CSS Grid and the New Autoprefixer

In Part 1 of this series, I debunked a few misconceptions that many people have around the Internet Explorer (IE) implementation of CSS grid. This article builds on that knowledge. It would be best to go back and read that article first if you haven’t already.

Today I’m going to be tackling the biggest misconception of all: that utilizing the IE implementation of CSS grid is extremely difficult. You can easily use CSS grid in IE right now without having to give it any sort of crappy fallback layout. It really isn’t that hard.

Article Series:

  1. Debunking Common IE Grid Misconceptions
  2. CSS Grid and the new Autoprefixer (This Post)
  3. Faking an auto-placement grid with gaps (Coming Soon!)

Giving IE a crappy fallback layout is a bit of a tough sell to clients. It is especially tough if this is for an intranet where 90% of the users are using IE. If you want IE compatibility, first you need to resign to the fact that you can’t use all of the fancy bells and whistles that can be found in the modern CSS grid spec. Don’t let this get you down though, you would be amazed by how much IE11 can handle, especially if it has some help from Autoprefixer.

Before I get too far into this, you need to know that this isn’t a general „how to use CSS grid” article. This article is for those that understand CSS grid, but are either too afraid or not allowed to use it, thanks to IE.

I’ll be assuming that you already have a good understanding of how to use CSS grid in modern browsers for this article. If you aren’t sure about how to use CSS grid yet, go watch Rachel Andrew’s excellent CSS grid video tutorial series. After that, go play some levels of the browser game Grid Garden to get some hands-on CSS grid experience. Once you understand the basics of how to use CSS grid in modern browsers, come back here and learn how to use it in a way that produces identical results in IE10 and 11.

Setting up Autoprefixer

Before you do anything else, you will want to get Autoprefixer up and running. Autoprefixer is a tool used for automatically adding browser-specific CSS prefixes to your CSS so that you don’t have to. It’s a bit like Babel but for CSS. (We have a great overview by the creator of Autoprefixer here on CSS Tricks.)

If you have never used Autoprefixer (or any build tool) before, then first install Node on your computer then follow this guide on how to get a basic workflow up and running. The bit in the guide specifically about Autoprefixer is found here. (I recommend the Gulp setup over Grunt if you aren’t sure which to go with.)

If you are already using Autoprefixer in your projects, make sure to update it to the latest version using this command:

npm i autoprefixer@latest -D

Some features used in the latest version of Autoprefixer are not supported in PostCSS version 5. This means that you may also need to update your version of PostCSS to at least version 6. If using Gulp, that means updating gulp-postcss to at least v7.0.0. If using Grunt, you may need to update grunt-postcss to at least v0.9.0.

Once you have an environment up and running, you will need to set the Autoprefixer grid setting to true. Without that setting turned on, Autoprefixer will not be able to do any of the cool stuff that I’m about to show you.

The exact method for turning the grid setting on depends a lot on how you compile your code. If you were using the guide that I linked to earlier, you would have seen this snippet of code:

var processorsArray = [
  // snipped for brevity
  require('autoprefixer')({ browsers: ['last 2 versions', 'ie 6-8', 'Firefox > 20']  })
];

Add grid: true to the options to turn grid prefixing on:

var processorsArray = [
  // snipped for brevity
  require('autoprefixer')({ grid: true, browsers: ['last 2 versions', 'ie 6-8', 'Firefox > 20']  })
];

By the way, the browser settings in the tutorial are pretty outdated and will be outputting far more prefixes than you actually need. Just going with ">1%" will mean that when a browser dies, Autoprefixer will automatically stop writing prefixes for that browser. It bases browser usage on global browser data retrieved from caniuse.com.

So, these are the Autoprefixer settings that you should end up using:

var processorsArray = [
  require('autoprefixer')({ grid: true, browsers: ['>1%'] })
];

Now onto the fun stuff! 😃

Autoprefixer has new super powers!

You may have read Rachel Andrew’s article „Should I try to use the IE implementation of CSS Grid Layout?” That article is all about CSS grid and understanding it’s support in IE10 and 11. It’s a great article and very useful for understanding IE’s limitations. Definitely still worth a read if you haven’t already checked it out. Note that it is extremely outdated in terms of its information on Autoprefixer though.

Autoprefixer has come a long way since Rachel wrote her IE11 CSS grid article and Autoprefixer now supports far more CSS grid translations. Take a look at this new, updated version of Rachel’s table based on Autoprefixer version 8.6.4. Note that items in bold represent a change in Autoprefixer support compared to Rachel’s article:

CSS Grid Property IE10 Implementation Autoprefixer? Notes
grid-template-columns -ms-grid-columns Yes
grid-template-rows -ms-grid-rows Yes
grid-template-areas NA Yes Autoprefixer uses this to understand what the grid looks like but does not add any extra properties.
grid-template NA Yes Shorthand for grid-template-columns, grid-template-rows, and grid-template-areas
grid-auto-columns NA No IE doesn’t support auto-placement
grid-auto-rows NA No IE doesn’t support auto-placement
grid-auto-flow NA No IE doesn’t support auto-placement
grid NA No See this GitHub issue as to why this isn’t supported
grid-row-start -ms-grid-row Yes1 Span syntax requires grid-row-end to be defined
grid-column-start -ms-grid-column Yes1 Span syntax requires grid-column-end to be defined
grid-row-end NA Yes1 grid-row-start must be defined
grid-column-end NA Yes1 grid-column-start must be defined
grid-row NA Yes1
grid-column NA Yes1
grid-area NA Yes2 Autoprefixer translates the grid area into column/row coordinates
grid-row-gap NA Yes3 Generates extra rows/columns in IE.
grid-column-gap NA Yes3 Generates extra rows/columns in IE.
grid-gap NA Yes3 Generates extra rows/columns in IE.
NA -ms-grid-column-span Yes Translated from grid-column-end and grid-area.
NA -ms-grid-row-span Yes Translated from grid-row-end and grid-area.
align-self -ms-grid-row-align Yes
justify-self -ms-grid-column-align Yes

Table disclaimers

I will cover these in far more detail later in the article:

  1. Autoprefixer cannot prefix negative integers.
  2. Each grid element must have unique area names.
  3. Autoprefixer only prefixes grid-gap if both grid-template-areas and grid-template-columns have been defined. It also cannot inherit grid-gap through media queries.

grid-template-areas is your new best friend

As you can see from the updated (and much more positive-looking) table, there are a lot of cool new features in Autoprefixer now. The most important of which is its support for grid-template-areas (and by extension grid-template). By supporting grid-template-areas, Autoprefixer now understands exactly what your grid looks like. This paved the way for Autoprefixer to also support (in a limited capacity) grid-gap.

Note that Autoprefixer still does not support the shortcut grid property. This was an intentional design decision that you can learn more about in this GitHub issue. The short story is that the grid property is not only a shortcut for the templating settings but also the auto-placement settings. Since IE can’t do auto-placement and the grid-template property can essentially do anything that the grid property can do (that IE can handle), it was decided that the grid property wasn’t worth supporting.

Back when Rachel wrote her article, this was the sort of grid code you would need to write in order to support IE:

/* IE-friendly source code 26 November 2016 */
/* Code needed to make Autoprefixer work properly */

.grid {
  display: grid;
  grid-template-columns: 1fr 10px 1fr;
  grid-template-rows: 100px 10px 100px;
}

.cell-A {
  grid-column: 1;
  grid-row: 1;
}

.cell-B {
  grid-column: 3;
  grid-row: 1;
}

.cell-C {
  grid-column: 1;
  grid-row: 3;
}

.cell-D {
  grid-column: 3;
  grid-row: 3;
}

Now, you can write code like this instead:

/* Today’s IE-friendly source code */
/* Autoprefixer can now make this code IE safe */

.grid {
  display: grid;
  grid-gap: 10px;
  grid-template:
    "a   b" 100px
    "c   d" 100px /
    1fr  1fr;
}

.cell-A {
  grid-area: a;
}

.cell-B {
  grid-area: b;
}

.cell-C {
  grid-area: c;
}

.cell-D {
  grid-area: d;
}

Autoprefixer will take the above code and translate it into this much more IE-friendly code for you:

/* Autoprefixer’s IE-friendly translation */

.grid {
 display: -ms-grid;
 display: grid;
 grid-gap: 10px;
 -ms-grid-rows: 100px 10px 100px;
 -ms-grid-columns: 1fr 10px 1fr;
 grid-template:
   "a   b" 100px
   "c   d" 100px /
   1fr  1fr;
}

.cell-A {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
  grid-area: a;
}

.cell-B {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
  grid-area: b;
}

.cell-C {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
  grid-area: c;
}

.cell-D {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
  grid-area: d;
}

Best of all, you can then simply change the grid-template or grid-template-areas property with a media query and Autoprefixer will automatically update all of the grid cell coordinates for you:

/* Changing a grid-template with a media-query */

.grid {
  display: grid;
  grid-gap: 10px;
  grid-template:
    "a   b" 100px
    "c   d" 100px
    "e   f" 100px /
    1fr  1fr;
}

@media (min-width: 600px){
  .grid {
    /* Autoprefixer 8.6.4 doesn't inherit grid gaps :( */
    grid-gap: 10px;
    grid-template:
      "a   b   c" 100px
      "d   e   f" 100px /
      1fr  1fr 1fr;
  }
}

.cell-A {
  grid-area: a;
}

.cell-B {
  grid-area: b;
}

.cell-C {
  grid-area: c;
}

.cell-D {
  grid-area: d;
}

.cell-E {
  grid-area: e;
}

.cell-F {
  grid-area: f;
}

The above gets translated into this IE-friendly code:

/* Autoprefixer’s IE-friendly media query translation */

.grid {
  display: -ms-grid;
  display: grid;
  grid-gap: 10px;
  -ms-grid-rows: 100px 10px 100px 10px 100px;
  -ms-grid-columns: 1fr 10px 1fr;
  grid-template:
    "a   b" 100px
    "c   d" 100px
    "e   f" 100px /
    1fr  1fr;
}

@media (min-width: 600px) {
  .grid {
    /* Autoprefixer 8.6.4 doesn't inherit gaps :( */
    grid-gap: 10px;
    -ms-grid-rows: 100px 10px 100px;
    -ms-grid-columns: 1fr 10px 1fr 10px 1fr;
    grid-template:
      "a   b   c" 100px
      "d   e   f" 100px /
      1fr  1fr 1fr;
  }
}

.cell-A {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
  grid-area: a;
}

.cell-B {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
  grid-area: b;
}

.cell-C {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
  grid-area: c;
}

.cell-D {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
  grid-area: d;
}

.cell-E {
  -ms-grid-row: 5;
  -ms-grid-column: 1;
  grid-area: e;
}

.cell-F {
  -ms-grid-row: 5;
  -ms-grid-column: 3;
  grid-area: f;
}

@media (min-width: 600px) {
  .cell-A {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
  }
  
  .cell-B {
    -ms-grid-row: 1;
    -ms-grid-column: 3;
  }
  
  .cell-C {
    -ms-grid-row: 1;
    -ms-grid-column: 5;
  }
  
  .cell-D {
    -ms-grid-row: 3;
    -ms-grid-column: 1;
  }
  
  .cell-E {
    -ms-grid-row: 3;
    -ms-grid-column: 3;
  }
  
  .cell-F {
    -ms-grid-row: 3;
    -ms-grid-column: 5;
  }
}

The duplicate media query might look a bit ugly but I assure you that this is the best possible way that Autoprefixer can handle the media query translation. The new IE grid cell coordinates could not be placed into the same media query as the grid template definition. Doing so would lead to one of two possible outcomes. One outcome is that the default grid cell positions would override the altered positions stated in the media query. This would cause the media query to have no effect in IE. The other outcome would be for Autoprefixer to shift all of the styles inside the media query (yes, that includes all of the styles that you wrote yourself) to another location in the style sheet, potentially causing horrific CSS specificity issues. You can learn more about the thinking behind this decision in the now closed GitHub issue for it.

Autoprefixer still can’t save you from everything

Even Superman can’t always save everyone and Autoprefixer is no different. While Autoprefixer is able to cut down on a lot of the workload involved in making our grids IE-compatible, it can’t fix everything. It can only translate things that IE can understand. These are the many critical things that you need to be aware of if you don’t want to open the site up in IE one day and have it blow up in your face.

Grid Gap has limited support

As you may have seen in that last example, grid-gap isn’t currently able to be inherited through media queries. If you want to use grid-gap, you will need to duplicate the grid-gap setting across all of the media queries where you define a grid template for that grid. There is an issue open on GitHub about this. Go give it a thumbs up if you want Autoprefixer to support it. Hopefully the issue is fixed soon.

That isn’t the only problem around using grid-gap though. It is only supported by Autoprefixer when both grid-template-areas and grid-template-columns have been defined.

Autoprefixer adds grid-gap support by using grid-template-areas to understand what your grid looks like. It then takes your grid-template-columns and grid-template-rows definitions and injects the grid-gap value between each row and column, creating extra rows and columns in IE.

If you try to use grid-gap on its own without grid-template-areas, Autoprefixer has no way of knowing what cell belongs to what grid. Without that critical knowledge, it cannot safely inject the extra columns and rows that IE needs.

That explains grid-template-areas but why do we also need to define grid-template-columns? Shouldn’t something like this be just as easy for Autoprefixer to translate?

.grid {
  display: grid;
  grid-gap: 20px;
  grid-template-areas:
    "a  b  c"
    "d  e  f";
}

.cell-a {
  grid-area: a;
}

.cell-f {
  grid-area: f;
}

Rows and columns in CSS grid default to a value of auto so can’t Autoprefixer just add something like -ms-grid-columns: auto 20px auto 20px auto;? It does that for rows, so why can’t it do the same thing for columns?

Well my inquisitive friend, I explained in Part 1 that auto in IE acts a bit differently to auto in modern browsers. When columns are set to auto in IE, they will always shrink down to the value of max-content. Modern grid columns, on the other hand, will expand to 1fr if there are no other fr units being used in that grid template declaration. This discrepancy can cause a massive difference in appearance between the modern and the IE version of a grid. The Autoprefixer team felt that it was too dangerous to make assumptions about this, so they made grid-template-columns a mandatory setting in order for grid-gap to take effect.

So that explains why Autoprefixer doesn’t support grid-gap when grid-template-columns is missing. If auto behaves so differently in IE, then why does Autoprefixer support grid-gap without the user explicitly having to define grid-template-rows? Isn’t that just as bad?

Not really. When you set display: grid; on something, it’s width will grow to the full width of its container. Its height, on the other hand, typically shrinks to the height of its content. Of course this isn’t always the case. There are a number of reasons why a grid might be taller than its content. If the grid is also a flex item and the flex container is taller than the grid, then that would be one reason why the grid might be taller than its content. In general though, if there are no other forces involved, then a grid container will always be the same height as its content.

Since the height of a typical grid is the same height as its content, in most cases, auto in both IE and modern browsers will behave identically to one another. It will only differ in functionality if the height of the grid exceeds the height of the content inside of it. For the best balance between user convenience and browser consistency, the Autoprefixer team made a choice. They decided that supporting a missing grid-template-rows property but not a missing grid-template-columns property was the best way to handle grid-gap support.

No auto-placement! No auto-placement! No auto-placement!

I really can’t say this enough. The most important thing to remember when using CSS grid in IE is that everything must be placed manually. The instant you start thinking about using auto-placement is the instant your site blows up in IE. I have a method for dealing with grids that have an unknown number of cells in them. I’m covering that in Part 3 of this series. The main thing to know right now is that if you want to use CSS grid in an IE-friendly way, you should only ever use it if there are a known number of cells for a known number of rows and columns.

It’s this lack of auto-placement in IE that makes having access to grid areas through Autoprefixer such a blessing. Instead of having to calculate all of the coordinates manually, you can instead name each cell and then let Autoprefixer do the math for you. When using media queries, you only need to redefine the grid template and autoprefixer will recalculate all of the coordinates for you. You rarely have to do any column or row counting. It’s great!

Area names must be unique

The ability to use grid-template-areas is one of Autoprefixer’s greatest strengths, though it has its limits. Autoprefixer does not have any access to the DOM. Due to this, Autoprefixer is entirely dependent on using the area name for understanding where each cell needs to be placed in the grid. This can cause clashes if you use the same area name twice in the same stylesheet.

Here is a small example. Early in the stylesheet, Grid Alpha has grid-template-areas: "delta echo". Later in the stylesheet, Grid Beta has grid-template-areas: "echo delta". We say that our grid cell belongs to area echo… so does it go in column 1 or 2?

.grid-alpha {
  grid-template-areas: "delta  echo";
}

.grid-beta {
  grid-template-areas: "echo  delta";
}

.grid-cell {
  /* What column does .grid-cell go in? */
  -ms-grid-column: ???;
  grid-area: echo;
}

Modern browsers know exactly what column to place the grid cell in because they have access to the DOM. If the cell is placed inside Grid Alpha, it will go in the first column. If it is placed in Grid Beta it will go in the second column. Autoprefixer can only read your CSS. It has no idea if the grid cell is placed in Grid Alpha or Grid Beta. All it knows is that the grid cell needs to be placed in the „echo” area. Autoprefixer resolves this conundrum by going with whichever one came last in the stylesheet. In this case, Autoprefixer will honor Grid Beta’s "echo delta" areas definition since it occurs last. If you placed the grid cell inside Grid Alpha, it would look great in modern browsers but it would be placed in the wrong column in IE.

This also means that you can’t really use the strategy of giving a component a single designated area name that is repeatedly referenced. Referencing that area name in more than one grid-template-areas property will break IE for sure. Every single area name across every single grid in your style sheet needs to be unique or Autoprefixer will freak the heck out.

Autoprefixer currently does not warn you when you have used a duplicate area name in your stylesheet. If you would like to start seeing warnings for duplicate area names, there is an issue open on GitHub that you can add your support to. In the meantime, the easiest way to ensure that each area name is unique is probably adopting a BEM style approach to naming the areas.

.grid-alpha {
  grid-template-areas: "grid-alpha__delta  grid-alpha__echo";
}

.grid-beta {
  grid-template-areas: "grid-beta__echo  grid-beta__delta";
}

.grid-cell {
  /* No more conflict :) */
  -ms-grid-column: 2;
  grid-area: grid-alpha__echo;
}

This can be pretty verbose if there are lots of columns and rows. You might want to shorten it down to an abbreviation instead. Of course, the less verbose your area names, the more chance there is of a conflict.

.grid-alpha {
  grid-template-areas: "ga_delta  ga_echo";
}

.grid-beta {
  grid-template-areas: "gb_echo  gb_delta";
}

.grid-cell {
  -ms-grid-column: 2;
  arid-area: ga_echo;
}

There is one major exception. Grid areas in media queries are allowed to be duplicates of other areas as long as the area names defined in the media query are targeted at the same element. Without this exception, it would be impossible to change the grid areas based on screen size. The main thing to remember is that each grid has to have its own set of unique area names that must not be shared with any other grids.

@media (min-width: 600px) {
  .grid-one {
    grid-template-areas:
      "alpha bravo"
      "alpha charlie";
   }
}

@media (min-width: 900px) {
  .grid-one {
    /* This is fine */
    /* It is targeting the same element */
    grid-template-areas:
      "alpha bravo charlie";
  }
}

@media (min-width: 900px) {
  /* NOT FINE! */
  /* The "alpha" area is being reused on a different element! */
  .grid-two {
    grid-template-areas:
      "alpha delta";
  }
}

Autoprefixer has limited column and row spanning support

There are only two properties in IE’s implementation of CSS grid that will help you span multiple columns. The main one being -ms-grid-column/row-span which tells IE how many columns/rows to span. The other being -ms-grid-column/row which tells IE where to start counting from.

.grid-cell {
  -ms-grid-column-span: 2; /* number of cells to span */
  -ms-grid-column: 1; /* starting cell */
}

In modern browsers, you have access to far more options.

Autoprefixer friendly

Out of the modern ways to span multiple cells, Autoprefixer fully supports the following. Feel free to use any of these methods as much as you like:

Specify a starting line and the number of lines to span (similar to IE):

.grid-cell {
  grid-column: 1 / span 2;
}

Specify an end line, then span backwards:

.grid-cell {
  grid-column: span 2 / 3;
}

Specify a starting line and an end line directly:

.grid-cell {
  grid-column: 1 / 3;
}

Specify only a number of cells to span using grid-column/row-end. Remember that IE can’t do auto-placement though. A starting line will still need to be specified elsewhere in the style sheet:

.grid-cell {
  /* Make sure you specify a starting line elsewhere */
  grid-column-end: span 2;
}

Specify only a starting line:

.grid-cell {
  /* The short way */
  grid-column: 1;

  /* The more verbose way */
  grid-column-start: 1;
}
Autoprefixer unfriendly. Here be dragons!

Now this is where Autoprefixer reaches its limit. The following methods are supported in modern browsers but are not supported by Autoprefixer. This is mainly due to Autoprefixer having no idea what grid the grid cell belongs to since it can only base its decisions on what is in the stylesheet.

Specify a starting line and how many lines from the end of the explicit grid to span:

.grid-cell {
  grid-column: 1 / -1;
}

Specify both the start and end line from the end of the explicit grid:

.grid-cell {
  grid-column: -3 / -1;
}

Specify only how many lines to span using the shorthand syntax (issue in GitHub):

.grid-cell {
  grid-column: span 2;
}

Specify only a backwards span (IE can’t span backwards):

.grid-cell {
  grid-column-start: span 2;
}

Specify only an end line (IE doesn’t understand end and Autoprefixer doesn’t know where the start is):

.grid-cell {
  grid-column-end: 3;
}

So, basically avoid counting backwards from the end of the grid and you’ll be fine. 😊

Avoid using line names for now

One of the cool features of modern grids is giving grid templates line names. Instead of using numbers to reference a line, you can give the line a name and reference that instead. Since Autoprefixer supports grid areas, you would think that they would also support line names. Unfortunately, that isn’t the case. As of version 8.6.4, Autoprefixer does not support line names (at the time of writing). Don’t worry though! It’s not that it is impossible to support (at least not entirely), it just hasn’t been a high priority for them. If you love using line names in your grids then let them know about it in the GitHub issue for it. Post your use cases and it will surely increase the priority of the feature. In the meantime, see if you can use grid areas instead for now.

Keep in mind that, if it is going to be implemented, then every line name across your stylesheet would need to be unique. Like with grid areas, Autoprefixer wouldn’t know what line name belongs to what grid. The only way it can tell is if every line name in the stylesheet is unique (excluding media queries).

You still need to test!

IE will behave itself most of the time as long as you follow all of the rules we’ve covered so far. That said, IE can still be a bit unpredictable. Just recently, I made a grid item a vertical flowing flex container and encountered an odd bug in IE11. The column width was set to minmax(min-content, 350px) but IE seemed to treat min-content like max-content in this circumstance. This completely broke the layout. Changing it to minmax(0, 350px) fixed the issue. Just goes to show that IE’s grid implementation isn’t quite perfect.

There are also times when you might accidentally forget to explicitly place grid cells. We build our grids in modern browsers because they have the nice grid development tools (especially Firefox). Modern browsers have auto-placement, though. This means that you might be halfway through building your grid then get called away for a moment. When you return to your desk, you see the layout looking beautiful in your browser, but you completely forget that you haven’t explicitly placed any grid cells yet. You move onto the next thing, blissfully unaware that you have left IE in a completely broken state.

Neither of those issues will reveal themselves until you test your site in IE11. Any time that you get a grid looking good in a modern browser, open it up in IE and double-check that it still looks the way you expect it to.

Autoprefixer control comments

Despite Autoprefixer’s best efforts, there are still some rare occasions when it seems like Autoprefixer is hindering you more than it is helping you. If you ever find yourself in a situation where it would be easier to not have Autoprefixer translating your code, you can turn Autoprefixer off using something called a „control comment.”

autoprefixer: off

This control comment will turn off Autoprefixer translations for the entire CSS block. It will not prefix any styles before or after the point where the comment is made.

/* Input CSS */

.normal-behavior {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.off-behavior {
  display: grid;
  /* autoprefixer: off */
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.normal-behavior-again {
  display: grid;
}
/* Output CSS */

.normal-behavior {
  display: -ms-grid;
  display: grid;
  -ms-grid-columns: 1fr 1fr;
  grid-template-columns: 1fr 1fr;
  -ms-grid-rows: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.off-behavior {
  display: grid;
  /* autoprefixer: off */
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.normal-behavior-again {
  display: -ms-grid;
  display: grid;
}

autoprefixer: ignore next

If you are after more of a scalpel than a sledge hammer, the /* autoprefixer: ignore next */ control comment is more for you. „Ignore next” will skip the next CSS declaration rather than ignoring the entire CSS block.

/* Input CSS */

.normal-behavior {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.ignore-next-behavior {
  display: grid;
  /* autoprefixer: ignore next */
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}
/* Output CSS */

.normal-behavior {
  display: -ms-grid;
  display: grid;
  -ms-grid-columns: 1fr 1fr;
  grid-template-columns: 1fr 1fr;
  -ms-grid-rows: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.ignore-next-behavior {
  display: -ms-grid;
  display: grid;
  /* autoprefixer: ignore next */
  grid-template-columns: 1fr 1fr;
  -ms-grid-rows: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

Pre-processor comments

If you are using Sass (or another CSS pre-processor) in your project, do not use the double slash (//) method for writing these comments. Sass will strip those comments out before Autoprefixer has a chance to translate the code.

/* Input SCSS */

.normal-behavior {
  display: grid;
}

.failed-off-behavior {
  // autoprefixer: off
  display: grid;
}

.successful-off-behavior {
  /* autoprefixer: off */
  display: grid;
}
/* Output CSS */

.normal-behavior {
  display: -ms-grid;
  display: grid;
}

.failed-off-behavior {
  display: -ms-grid;
  display: grid;
}

.successful-off-behavior {
  /* autoprefixer: off */
  display: grid;
}

Let’s recap!

So, after all that, here is the list of all the things you need to remember to be an IE11 CSS grid master:

  • Use the latest version of Autoprefixer (npm i autoprefixer@latest -D).
  • Turn on the Autoprefixer grid setting.
  • Do not attempt to use auto-placement, every cell must be placed manually.
  • Use grid-template-areas as your primary method for placing grid cells into position.
  • Use grid-template (not grid) as a shortcut.
  • Don’t use duplicate area names unless they are inside a media query targeting the same element. Also, give this GitHub issue your support if you would like Autoprefixer to warn you when you have used a duplicate area name.
  • You can use grid-gap as long as you define both grid-template-areas and grid-template-columns.
  • grid-gap is currently not inherited through media queries. Go give this GitHub issue a thumbs up if you want to write less grid-gap code.
  • When spanning multiple columns and rows, avoid counting backwards from the end of the grid. Autoprefixer doesn’t understand your grids well enough to supports this.
  • Avoid using line names for now. Give this GitHub issue a thumbs up if you want to start using them.
  • Use control comments /* autoprefixer: off */ and /* autoprefixer: ignore next */ to prevent Autoprefixer from translating certain parts of your style-sheet that are causing problems.
  • Don’t forget to test!

…and from Part 1:

  • IE does have an implicit grid.
  • IE supports repeat functionality.
  • minmax(), min-content and max-content are all natively supported.
  • fit-content() isn’t natively supported but you can work around this with auto and max-width settings.
  • IE auto is not equal to auto in modern browsers.

If you have any questions, or if this really helped you out, let me know in the comments! I’m also @Daniel_Tonon on Twitter. 😁

Up next…

In Part 3, I will be covering how to make a fully responsive flexbox-based grid in IE11. This flexbox technique even replicates grid-gap functionality!

Article Series:

  1. Debunking Common IE Grid Misconceptions
  2. CSS Grid and the new Autoprefixer (This Post)
  3. Faking an auto-placement grid with gaps (Coming Soon!)

The post CSS Grid in IE: CSS Grid and the New Autoprefixer appeared first on CSS-Tricks.

Clearfix: A Lesson in Web Development Evolution

Post pobrano z: Clearfix: A Lesson in Web Development Evolution

The web community has, for the most part, been a spectacularly open place. As such, a lot of the best development techniques happen right out in the open, on blogs and in forums, evolving as they’re passed around and improved. I thought it might be fun (and fascinating) to actually follow this creative exchange all the way through. To take a look at a popular CSS trick, the clearfix, and find out exactly how a web design technique comes to be.

The clearfix, for those unaware, is a CSS hack that solves a persistent bug that occurs when two floated elements are stacked next to each other. When elements are aligned this way, the parent container ends up with a height of 0, and it can easily wreak havoc on a layout. All you might be trying to do is position a sidebar to the left of your main content block, but the result would be two elements that overlap and collapse on each other. To complicate things further, the bug is inconsistent across browsers. The clearfix was invented to solve all that.

An early illustration of the issue from Position is Everything

But to understand the clearfix, you have to go back even further, to the 2004 and a particular technique called the Holly hack.

2004: The Holly Hack and the origin of Clearfix

The Holly hack is named for its creator, Holly Begevin, a developer and blogger at CommunityMX. The Holly hack combines two different CSS techniques that worked in the days of Netscape Navigator and Internet Explorer (IE) 4 to solve some layout issues. Begevin noticed that if you apply a height of just 1% to each of the floated elements in the above scenario, the problem would actually fix itself (and only because it activated an entirely different bug) in Internet Explorer for Windows.

Unfortunately, the 1% trick didn’t work on a Mac. For that, Begevin added a conditional comment which used a backslash inside of her CSS, which strangely enough, blocked individual CSS rules from IE for Mac in the old days. The result was a CSS trick that looked like this:

/* Hides from IE5-mac \*/
* html .buggybox {height: 1%;}
/* End hide from IE5-mac */

Odd, I know, but it only gets more complicated.

That same year, in May, there were a few more browsers to deal with, and not all of them could be patched with one line of CSS. Tony Aslett posted a new thread to his message board, CSS Creator, proposing a new approach. He called the trick a clearfix because it centered around clearing the floated elements to fix the issue.

Aslett’s approach took advantage of what were, at the time, still very new CSS pseudo-selectors (specifically :after) to automatically clear out two floated elements. There was one pretty massive drawback in Aslett’s first version of the clearfix. Having never heard of the Holly Hack, Aslett’s code required a bit of JavaScript to fix issues that were specifically popping up on IE for Mac. In those days, JavaScript was a relatively untested technology, and relying on it in such a fundamental way was less than ideal.

Thankfully, the web is a place of iteration, and one message board user pointed Aslett in the direction of the aforementioned Holly Hack. By sprinkling in Begevin’s conditional comment, Aslett was able to make his code work in just about every browser with no JavaScript at all.

.floatcontainer:after {
  content: "."; 
  display: block; 
  height: 0; 
  clear: both; 
  visibility: hidden;
}

/* Mark Hadley's fix for IE Mac */     
.floatcontainer {
  display: inline-block;
}

/* Hides from IE Mac \*/
* html .floatcontainer {height: 1%;}
.floatcontainer{display:block;}
/* End Hack */

If you want to pick apart an important slice of web history and innovation, the discussion that followed Aslett’s post about clearfix is a great place to start. One by one, people began to experiment with the new technique, testing it in obscure browsers and devices. When they were done, they’d post their results to the message thread, sometimes alongside a helpful tweak.

For example, the user zulaica pointed out that in Mozilla browsers, the bottom border of floated elements had to be explicitly defined. User pepejeria noticed that you could leave out the dot from content, and user co2 tested the clearfix in the very first version of Apple’s Safari browser. Each time, Aslett would update his code a bit until, after more than a few rapid iterations, the clearfix was ready and, thanks to the community, pretty darn bulletproof.

2006: Clearfix gets an update

But browsers kept on advancing. Some of the more quirky bits of the clearfix code worked because of bugs that were built into older browsers. As browsers patched those bugs, in brand new versions, the clearfix stopped working. When IE 7 came out at the end of 2006, a few adjustments to the technique were needed to make it work.

Fortunately, John “Big John” Gallant was maintaining a page on his blog Position is Everything with an up to date version of the clearfix. After getting some feedback from his readers, Gallant updated his blog to reflect a few new fixes for IE 7 using a new kind of conditional comment that would work inside of Internet Explorer.

<style type="text/css">

  .clearfix:after {
    content: ".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
  }

</style><!-- main stylesheet ends, CC with new stylesheet below... -->

<!--[if IE]>
<style type="text/css">
  .clearfix {
    zoom: 1;     /* triggers hasLayout */
  }  /* Only IE can see inside the conditional comment
    and read this CSS rule. Don't ever use a normal HTML
    comment inside the CC or it will close prematurely. */
</style>
<![endif]-->

And once again, users took to their own browsers to test out the latest code to ensure it worked everywhere. And once again, for a time, it did.

2010: Clearfix Reloaded

In fact, this iteration of the clearfix would last until about 2010, when the team at the Yahoo! User Interface Library (YUI) noticed a few issues with the clearfix. The Holly Hack, for instance, was now long gone (IE 5 was but a distance memory), and after switching the box model, margins were handled a bit differently by modern browsers.

But the folks at YUI still needed to line up one element next to another. In fact, the need had only increased, as designers experimented with more advanced grid layouts. In 2010, there were very little options for grid layout, so clearfix had to work. They eventually came up with a few additional tweaks to the CSS ruleset, most notably by taking of advantage of both available pseudo-selectors (:before and :after), to clear out any margins. They posted their new take to their own blog and called it “clearfix reloaded.”

.clearfix:before,
.clearfix:after {
  content: ".";    
  display: block;    
  height: 0;    
  overflow: hidden;        
}
.clearfix:after { clear: both; }
.clearfix { zoom: 1 ;} /* IE < 8 */

2011: The Micro Clearfix

But even when it was published in 2010, clearfix reloaded brought with it some unnecessary baggage from older browsers. The height equals 0 trick wasn’t really a requirement anymore, and in fact, the trick was a lot more reliable when display: table was used instead. People began swapping various variations on the technique on Twitter and on blogs. About a year after the release of clearfix reloaded, developer Nicolas Gallagher compiled these variations into a much more compact version of the hack, which he appropriately labeled the micro clearfix.

After years of back and forth and slight adjustments, the clear fix now required just four CSS rules:

/*
 * For modern browsers
 * 1. The space content is one way to avoid an Opera bug when the
 *  contenteditable attribute is included anywhere else in the document.
 *  Otherwise it causes space to appear at the top and bottom of elements
 *  that are clearfixed.
 * 2. The use of `table` rather than `block` is only necessary if using
 * `:before` to contain the top-margins of child elements.
 */
.cf:before,
.cf:after {
  content: " "; /* 1 */
  display: table; /* 2 */
}

.cf:after {
  clear: both;
}

/*
 * For IE 6/7 only
 * Include this rule to trigger hasLayout and contain floats.
 */
.cf {
  zoom: 1;
}

The End of Clearfix?

These days, almost 15 years after it was first proposed, the clearfix is losing a bit of relevance. CSS Grid and Flexbox are filling in the gaps for advanced layout on the web. In January of 2017, Rachel Andrew wrote an article for her blog titled „The end of the clearfix hack?” In it, she describes a way to replace the clearfix hack with a single line of code using a new display mode rule known as flow-root.

.container {
  display: flow-root;
}

We are approaching the day when clearfix will no longer be necessary at all.

Even without flow-root, there’s lots of ways to make a grid these days. If you were just starting out on the web, there’d be little reason to even learn about it. That’s a good thing! From the beginning it was always meant as a workaround to make the best of what was available. The irony being that without the dedication and experimentation of the developers who hacked away on the clearfix for so many years, we wouldn’t have the tools today to never have to rely on it again.

Enjoy learning about web history with stories just like this? Jay Hoffmann has a weekly newsletter called The History of the Web you can sign up for here.

The post Clearfix: A Lesson in Web Development Evolution appeared first on CSS-Tricks.

Vue + TypeScript: A Match Made in Your Code Editor

Post pobrano z: Vue + TypeScript: A Match Made in Your Code Editor

Vue is so hot right now and I’ve been thinking of doing a serious project with it since quite a while, so when the opportunity popped up, I hopped in. But there was a little problem — one of the requirements of the project was to write it in TypeScript. At first, I was super stressed about how I was going to ever get started on this combo, but vue-cli made it so easy.

I’d be lying if I said this ride was super smooth. There were frustrations, hours of staring at the screen and some fistbumps with my table but after working with Vue + TypeScript for over a month now, I can say it was worth it — and if I had to code another app with Vue, I wouldn’t do it without TypeScript.

Prerequisites

This article is about pairing Vue and TypeScript and assumes some basic knowledge of both. If you haven’t had a chance to play with them yet and are curious, Vue has a great guide, and the TypeScript docs are a great place to start.

We need to have vue-cli installed globally, so we can quickly spin up Vue project. Install vue-cli by running the following command in your terminal:

npm install -g @vue/cli

Once we have that installed, we’re good to go. If you don’t have TypeScript installed, we don’t need to do that beforehand, as vue-cli takes care of that when you start a new project and choose TypeScript there.

Getting Started

Now that we have vue-cli installed, all we need to do to get a project with Vue + TypeScript started is to run vue create. While creating a new project, choose TypeScript and we’re good to go.

vue create <app-name>

Here’s the result once our project spins up:

vue-cli also provides us the ability to choose Babel along with TypeScript for polyfills, CSS preprocessors, linter, unit testing libraries (I picked Jest, go Jest!) along with other config. You can even save your choices in a preset, to use it later, for another project.

Here’s a rundown of the handy questions you’ll get asked to configure the project:

One thing I’d like to mention is that vue-cli 3.0 comes with a user interface which makes it even more easy to create a new project. Run vue ui in terminal and vue-cli opens a UI where you can set up a new project.

What’s in the Box

After vue-cli is done, we get a nice directory structure of our app with all the setup already done.

  • tsconfig.json: This is all set up and we can edit it to suit our requirements.
  • shims-vue.d.ts: These shims are already set up to help TypeScript understand .vue files (Single File Components) when they are imported.
  • vue-property-decorator: If you choose to use class-style components, vue-cli adds this plugin so we can use all sorts of decorators. This comes quite handy and make the code more readable.
  • Class components: If you choose to use them, vue-cli sets the stage for you. Bear in mind that you would still need to register router hooks so class components can resolve them.

Sass Setup

One thing that I needed to set up and I wish was included out of the box was shared Sass partials. To avoid importing my Sass variables and mixins in every component, I had to load them in vue.config.js. shared.scss is the file exporting all variables and mixins that are used within the app.

Here’s where I landed with my Sass configuration:

chainWebpack: (config) => {
   config
     .module
     .rule('vue')
     .uses
     .get('vue-loader')
     .tap(({ loaders, loaders: { scss }, ...options }) => ({
       ...options,
       loaders: {
         ...loaders,
         scss: [
           ...scss,
           {
             loader: 'sass-resources-loader',
             options: {
               resources: [
                 './src/styles/shared.scss',
               ],
             },
           },
         ],
       },
     }));

Vue Property Decorator

The vue-property-decorator package exposes Vue properties and makes them available to use as decorators. In my application, I ended up using only @Component, @Prop, @Watch but there are others such as @Emit, @Inject and @Model, that make your code much more verbose when used extensively.

Vuex

Vuex has typings…nuff said! Vuex supports TypeScript to boot and, at first, I didn’t even know it. I started to look for proper ways to combine Vuex with TypeScript and stumbled upon Alex Jover Morales’ egghead.io course on Vue.js State Management with Vuex and TypeScript. It helped me understand the correct way of managing Vuex state when using TypeScript.

For example:

// actions.ts
import { ActionTree } from 'vuex';
import { RootState, ModuleState } from '@/types';


const actions: ActionTree<ModuleState, RootState> = {
  // all your actions go here
};

Vuex-class

This is yet another thing that I didn’t know existed when I first started but know wish I had found it sooner. I was creating getters for almost everything, but that didn’t feel right. I started looking for better ways to do this and found an interesting article by Francesco Vitullo which cleared up a few things for me. That’s where I found out about vuex-class which provides decorators for all vuex mappers.

So, now instead of writing a new getter for simply accessing a property in state, I could do this:

import {
  State,
} from 'vuex-class'

@Component
export class MyComp extends Vue {
  @State(state => state.bar) stateBar
}

Development Experience With VS Code

With TypeScript, the coding experience on VS Code is so much better. There is no going back and forth to check what mutation types I declared in mutation-types.ts because VS Code can recognize them and suggest correct ones as I type.

The same goes for modifying state in mutations — with TypeScript, the editor knew what my state structure looks like and suggests correct properties.

If you’re using VS Code, I strongly recommend using the Vetur plugin because it provides Vue tooling and comes with other bells and whistles like syntax highlighting (this works great with Vue single file components) and linting right out of the box.

Final Thoughts

Just like everything else in the JavaScript ecosystem, Vue + TypeScript still has a long way to go. For example, I could not use vuelidate because it doesn’t have typings. But thankfully vee-validate provided a workaround, so I didn’t have to go down the difficult road of writing those myself.

In conclusion, I found the development experience to be much smoother and VS Code is a totally different beast when you work with TypeScript. I don’t really need to sing the praises of Vue — it is very easy to pick up and start building and saves days trying to wrap your head around the inner workings of a framework.

The post Vue + TypeScript: A Match Made in Your Code Editor appeared first on CSS-Tricks.

Better rendering for variable fonts

Post pobrano z: Better rendering for variable fonts

I was messing around with a variable font the other day and noticed this weird rendering issue in the latest version of Chrome where certain parts of letterforms were clipping into each other in a really weird way. Thankfully, though, Stephen Nixon has come to the rescue with a temporary hack to fix the issue which using a text-shadow on the text that’s using the variable font:

.variable-font {
  text-shadow: 0 0 0 #000; /* text color goes last here */
} 

Once you do that, you shouldn’t be able to see those weird clip marks in the letterforms anymore. Yeah, it feels pretty hacky but I’m sure this rendering bug will be fixed relatively soon. It doesn’t look like it affects other browsers, as far as I can tell.

Direct Link to ArticlePermalink

The post Better rendering for variable fonts appeared first on CSS-Tricks.