Theme Assets

  • ThemeColor is a NSColor subclass that dynamically changes its colors whenever a new theme is make current.

    Theme-aware means you don’t need to check any conditions when choosing which color to draw or set on a control. E.g.:

    myTextField.textColor = ThemeColor.myContentTextColor
    
    ThemeColor.myCircleFillColor.setFill()
    NSBezierPath(rect: bounds).fill()
    

    The text color of myTextField will automatically change when the user switches a theme. Similarly, the drawing code will draw with different color depending on the selected theme. Unless some drawing cache is being done, there’s no need to refresh the UI after changing the current theme.

    You can also define a color to be a pattern image using NSColor(patternImage:).

    Defining theme-aware colors

    The recommended way of adding your own dynamic colors is as follows:

    1. Add a ThemeColor class extension (or TKThemeColor category on Objective-C) to add class methods for your colors. E.g.:

      In Swift:

      extension ThemeColor {
      
        static var brandColor: ThemeColor { 
          return ThemeColor.color(with: #function)
        }
      
      }
      

      In Objective-C:

      @interface TKThemeColor (Demo)
      
      + (TKThemeColor*)brandColor;
      
      @end
      
      @implementation TKThemeColor (Demo)
      
      + (TKThemeColor*)brandColor {
        return [TKThemeColor colorWithSelector:_cmd];
      }
      
      @end
      
    2. Add Class Extensions on any Theme you want to support (e.g., LightTheme and DarkTheme - TKLightTheme and TKDarkTheme on Objective-C) to provide instance methods for each theme color class method defined on (1). E.g.:

      In Swift:

      extension LightTheme {
      
        var brandColor: NSColor {
          return NSColor.orange
        }
      
      }
      
      extension DarkTheme {
      
        var brandColor: NSColor {
          return NSColor.white
        }
      
      }
      

      In Objective-C:

      @interface TKLightTheme (Demo) @end
      
      @implementation TKLightTheme (Demo)
      
         - (NSColor*)brandColor
         {
             return [NSColor orangeColor];
         }
      
      @end
      
      @interface TKDarkTheme (Demo) @end
      
      @implementation TKDarkTheme (Demo)
      
         - (NSColor*)brandColor
         {
             return [NSColor whiteColor];
         }
      
      @end
      
    3. If supporting UserTheme‘s, define properties on user theme files (.theme) for each theme color class method defined on (1). E.g.:

      displayName = Sample User Theme
      identifier = com.luckymarmot.ThemeKit.SampleUserTheme
      darkTheme = false
      
      brandColor = rgba(96, 240, 12, 0.5)
      

    Overriding system colors

    Besides your own colors added as ThemeColor class methods, you can also override NSColor class methods so that they return theme-aware colors. The procedure is exactly the same, so, for example, if adding a method named labelColor to a ThemeColor extension, that method will be overriden in NSColor and the colors from Theme subclasses will be used instead. In sum, calling NSColor.labelColor will return theme-aware colors.

    You can get the full list of available/overridable color methods (class methods) calling NSColor.colorMethodNames().

    At any time, you can check if a system color is being overriden by current theme by checking the NSColor.isThemeOverriden property (e.g., NSColor.labelColor.isThemeOverriden).

    When a theme does not override a system color, the original system color will be used instead. E.g., you have overrided ThemeColor.labelColor, but currently applied theme does not implement labelColor -> original labelColor will be used.

    Fallback colors

    With the exception of system overrided named colors, which defaults to the original system provided named color when theme does not specifies it, unimplemented properties/methods on target theme class will default to fallbackForegroundColor and fallbackBackgroundColor, for foreground and background colors respectively. These too, can be customized per theme.

    Please check ThemeGradient for theme-aware gradients and ThemeImage for theme-aware images.

    See more

    Declaration

    Swift

    open class ThemeColor: NSColor
  • ThemeGradient is a NSGradient subclass that dynamically changes its colors whenever a new theme is make current.

    Theme-aware means you don’t need to check any conditions when choosing which gradient to draw. E.g.:

    ThemeGradient.rainbowGradient.draw(in: bounds, angle: 0)
    

    The drawing code will draw with different gradient depending on the selected theme. Unless some drawing cache is being done, there’s no need to refresh the UI after changing the current theme.

    Defining theme-aware gradients

    The recommended way of adding your own dynamic gradients is as follows:

    1. Add a ThemeGradient class extension (or TKThemeGradient category on Objective-C) to add class methods for your gradients. E.g.:

      In Swift:

      extension ThemeGradient {
      
          static var brandGradient: ThemeGradient {
             return ThemeGradient.gradient(with: #function)
          }
      
      }
      

      In Objective-C:

      @interface TKThemeGradient (Demo)
      
      + (TKThemeGradient*)brandGradient;
      
      @end
      
      @implementation TKThemeGradient (Demo)
      
      + (TKThemeGradient*)brandGradient {
         return [TKThemeGradient gradientWithSelector:_cmd];
      }
      
      @end
      
    2. Add Class Extensions on any Theme you want to support (e.g., LightTheme and DarkTheme - TKLightTheme and TKDarkTheme on Objective-C) to provide instance methods for each theme gradient class method defined on (1). E.g.:

      In Swift:

      extension LightTheme {
      
          var brandGradient: NSGradient {
             return NSGradient(starting: NSColor.white, ending: NSColor.black)
          }
      
          }
      
          extension DarkTheme {
      
          var brandGradient: NSGradient {
             return NSGradient(starting: NSColor.black, ending: NSColor.white)
          }
      
      }
      

      In Objective-C:

      @interface TKLightTheme (Demo) @end
      
      @implementation TKLightTheme (Demo)
      
      - (NSGradient*)brandGradient
      {
         return [[NSGradient alloc] initWithStartingColor:[NSColor whiteColor] endingColor:[NSColor blackColor]];
      }
      
      @end
      
      @interface TKDarkTheme (Demo) @end
      
      @implementation TKDarkTheme (Demo)
      
      - (NSGradient*)brandGradient
      {
         return [[NSGradient alloc] initWithStartingColor:[NSColor blackColor] endingColor:[NSColor whiteColor]];
      }
      
      @end
      
    3. If supporting UserTheme‘s, define properties on user theme files (.theme) for each theme gradient class method defined on (1). E.g.:

      displayName = Sample User Theme
      identifier = com.luckymarmot.ThemeKit.SampleUserTheme
      darkTheme = false
      
      orangeSky = rgb(160, 90, 45, .5)
      brandGradient = linear-gradient($orangeSky, rgb(200, 140, 60))
      

    Fallback colors

    Unimplemented properties/methods on target theme class will default to fallbackGradient. This too, can be customized per theme.

    Please check ThemeColor for theme-aware colors and ThemeImage for theme-aware images.

    See more

    Declaration

    Swift

    open class ThemeGradient: NSGradient
  • ThemeImage is a NSImage subclass that dynamically changes its colors whenever a new theme is make current.

    Theme-aware means you don’t need to check any conditions when choosing which image to draw. E.g.:

    ThemeImage.logoImage.draw(in: bounds)
    

    The drawing code will draw with different image depending on the selected theme. Unless some drawing cache is being done, there’s no need to refresh the UI after changing the current theme.

    Defining theme-aware images

    The recommended way of adding your own dynamic images is as follows:

    1. Add a ThemeImage class extension (or TKThemeImage category on Objective-C) to add class methods for your images. E.g.:

      In Swift:

      extension ThemeImage {
      
          static var logoImage: ThemeImage {
              return ThemeImage.image(with: #function)
          }
      
      }
      

      In Objective-C:

      @interface TKThemeImage (Demo)
      
      + (TKThemeImage*)logoImage;
      
      @end
      
      @implementation TKThemeImage (Demo)
      
      + (TKThemeImage*)logoImage {
          return [TKThemeImage imageWithSelector:_cmd];
      }
      
      @end
      
    2. Add Class Extensions on any Theme you want to support (e.g., LightTheme and DarkTheme - TKLightTheme and TKDarkTheme on Objective-C) to provide instance methods for each theme image class method defined on (1). E.g.:

      In Swift:

      extension LightTheme {
      
          var logoImage: NSImage? {
              return NSImage(named: "MyLightLogo")
          }
      
      }
      
      extension DarkTheme {
      
          var logoImage: NSImage? {
              return NSImage(contentsOfFile: "somewhere/MyDarkLogo.png")
          }
      
      }
      

      In Objective-C:

      @interface TKLightTheme (Demo) @end
      
      @implementation TKLightTheme (Demo)
      
      - (NSImage*)logoImage
      {
          return [NSImage imageNamed:@"MyLightLogo"];
      }
      
      @end
      
      @interface TKDarkTheme (Demo) @end
      
      @implementation TKDarkTheme (Demo)
      
      - (NSImage*)logoImage
      {
          return [NSImage alloc] initWithContentsOfFile:@"somewhere/MyDarkLogo.png"];
      }
      
      @end
      
    3. If supporting UserTheme‘s, define properties on user theme files (.theme) for each theme image class method defined on (1). E.g.:

      displayName = Sample User Theme
      identifier = com.luckymarmot.ThemeKit.SampleUserTheme
      darkTheme = false
      
      logoImage = image(named:MyLogo)
      //logoImage = image(file:../some/path/MyLogo.png)
      

    Fallback images

    Unimplemented properties/methods on target theme class will default to fallbackImage. This too, can be customized per theme.

    Please check ThemeColor for theme-aware colors and ThemeGradient for theme-aware gradients.

    See more

    Declaration

    Swift

    open class ThemeImage: NSImage