diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,6 @@
+# Changelog
+
+## 1.0.0
+
+### [Added]
+- Initial version
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2020, Jonas Carpay
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,25 @@
+## tasty-focus
+
+Simple focus mechanism for `tasty`, similar to `hspec`.
+
+Mark the root of your test tree with `withFocus`.
+Then, if any of the subtrees of your test suite are marked with `focus`, only those test trees will be run.
+
+The intended use case is similar to `--pattern`, but for quick `ghcid`-based feedback loops.
+Using `focus` will throw a deprecation warning, so that together with `-Werror` you can check that you don't accidentally leave tests focused on CI.
+
+### Example
+In this example, only `barTests` will run. Removing either `focus` or `withFocus` will run the entire tree again.
+
+```haskell
+main :: IO ()
+main = defaultMain . withFocus $
+  testGroup "tests"
+    [ fooTests
+    , testGroup "subgroup"
+      [ focus barTests
+      , bazTests
+	  ]
+	, quuxTests
+    ]
+```
diff --git a/src/Test/Tasty/Focus.hs b/src/Test/Tasty/Focus.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/Tasty/Focus.hs
@@ -0,0 +1,54 @@
+-- | Simple focus mechanism for @tasty@, similar to @hspec@.
+-- Mark the root of your test tree with 'withFocus'.
+-- Then, if any of the subtrees of your test suite are marked with 'focus', only those test trees will be run.
+module Test.Tasty.Focus
+  ( withFocus,
+    focus,
+  )
+where
+
+import Data.Monoid
+import Data.Tagged
+import Test.Tasty
+import Test.Tasty.Options
+import Test.Tasty.Runners
+
+data Focused = Focused | NotFocused
+
+instance IsOption Focused where
+  defaultValue = NotFocused
+  parseValue _ = Nothing
+  optionName = Tagged "focused"
+  optionHelp = Tagged "focused"
+
+anyFocused :: TestTree -> Bool
+anyFocused = getAny . foldTestTree tfold mempty
+  where
+    tfold = trivialFold {foldSingle = \opts _ _ -> Any (focusedOpts opts)}
+    focusedOpts opts = case lookupOption opts of
+      Focused -> True
+      NotFocused -> False
+
+{-# WARNING focus "Focusing tests... don't forget to re-enable your entire test suite!" #-}
+
+-- | Intended to be used at the root of your test suite.
+--   If any of the subtrees are focused, filter out all non-focused subtrees.
+--   If there are no focused subtrees, return the entire tree.
+withFocus :: TestTree -> TestTree
+withFocus tree = if anyFocused tree then go tree else tree
+  where
+    go (PlusTestOptions f t) = case lookupOption (f mempty) of
+      NotFocused -> TestGroup "ignored" []
+      Focused -> PlusTestOptions f t
+    go (TestGroup n t) = TestGroup n (fmap go . filter anyFocused $ t)
+    go (SingleTest n t) = SingleTest n t
+    go (WithResource s k) = WithResource s (go . k)
+    go (AskOptions f) = AskOptions (go . f)
+    go (After d e t) = After d e (go t)
+
+-- | Marks the tree as focused, as long as none of its subtrees are focused.
+focus :: TestTree -> TestTree
+focus tree =
+  if anyFocused tree
+    then tree
+    else testGroup "focused" [PlusTestOptions (setOption Focused) tree]
diff --git a/tasty-focus.cabal b/tasty-focus.cabal
new file mode 100644
--- /dev/null
+++ b/tasty-focus.cabal
@@ -0,0 +1,44 @@
+cabal-version:   2.4
+name:            tasty-focus
+version:         1.0.0
+license:         BSD-3-Clause
+build-type:      Simple
+license-file:    LICENSE
+author:          Jonas Carpay
+maintainer:      Jonas Carpay <jonascarpay@gmail.com>
+copyright:       2020 Jonas Carpay
+tested-with:     GHC ==8.10.2
+synopsis:        Simple focus mechanism for tasty
+description:     Simple focus mechanism for tasty, similar to hspec.
+extra-doc-files:
+  CHANGELOG.md
+  README.md
+
+common common-options
+  build-depends:    base >=4.9 && <5
+  default-language: Haskell2010
+  ghc-options:
+    -Wall -Wcompat -Widentities -Wincomplete-uni-patterns
+    -Wincomplete-record-updates -Wredundant-constraints
+    -fhide-source-paths -Wpartial-fields
+
+library
+  import:          common-options
+  hs-source-dirs:  src
+  exposed-modules: Test.Tasty.Focus
+  build-depends:
+    , tagged
+    , tasty
+
+test-suite tasty-focus-test
+  import:         common-options
+  type:           exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is:        Spec.hs
+  build-depends:
+    , tasty
+    , tasty-expected-failure
+    , tasty-focus
+    , tasty-hunit
+
+  ghc-options:    -threaded -rtsopts -with-rtsopts=-N
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,53 @@
+import Test.Tasty.Focus
+import Test.Tasty.ExpectedFailure
+import Test.Tasty
+import Test.Tasty.HUnit
+
+failure :: TestTree
+failure = testCase "failure" $ assertFailure "test failure"
+
+success :: TestTree
+success = testCase "success" $ pure ()
+
+main :: IO ()
+main =
+  defaultMain $
+    testGroup
+      "tasty-focus"
+      [ expectFail . withFocus $
+          testGroup
+            "no focus runs everything"
+            [ failure
+            ],
+        expectFail . withFocus . focus $
+          testGroup
+            "focus entire tree runs everything"
+            [ failure
+            ],
+        withFocus $
+          testGroup
+            "focus out failure"
+            [ focus success,
+              failure
+            ],
+        withFocus $
+          testGroup
+            "deep focus"
+            [ testGroup
+                "nest"
+                [ focus success,
+                  failure
+                ],
+              failure
+            ],
+        withFocus . focus $
+          testGroup
+            "nested focus precedence"
+            [ testGroup
+                "nest"
+                [ focus success,
+                  failure
+                ],
+              failure
+            ]
+      ]
